[llvm] r313264 - [InstSimplify] fold sdiv/srem based on compare of dividend and divisor

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 14 07:59:07 PDT 2017


Author: spatel
Date: Thu Sep 14 07:59:07 2017
New Revision: 313264

URL: http://llvm.org/viewvc/llvm-project?rev=313264&view=rev
Log:
[InstSimplify] fold sdiv/srem based on compare of dividend and divisor

This should bring signed div/rem analysis up to the same level as unsigned. 
We use icmp simplification to determine when the divisor is known greater than the dividend.

Each positive test is followed by a negative test to show that we're not overstepping the boundaries of the known bits.
There are extra tests for the signed-min-value special cases.

Alive proofs:
http://rise4fun.com/Alive/WI5

Differential Revision: https://reviews.llvm.org/D37713

Modified:
    llvm/trunk/lib/Analysis/InstructionSimplify.cpp
    llvm/trunk/test/Transforms/InstCombine/div.ll
    llvm/trunk/test/Transforms/InstSimplify/exact-nsw-nuw.ll
    llvm/trunk/test/Transforms/InstSimplify/signed-div-rem.ll

Modified: llvm/trunk/lib/Analysis/InstructionSimplify.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/InstructionSimplify.cpp?rev=313264&r1=313263&r2=313264&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/InstructionSimplify.cpp (original)
+++ llvm/trunk/lib/Analysis/InstructionSimplify.cpp Thu Sep 14 07:59:07 2017
@@ -917,20 +917,54 @@ static bool isICmpTrue(ICmpInst::Predica
 
 /// Return true if we can simplify X / Y to 0. Remainder can adapt that answer
 /// to simplify X % Y to X.
-static bool isDivZero(Value *Op0, Value *Op1, const SimplifyQuery &Q,
+static bool isDivZero(Value *X, Value *Y, const SimplifyQuery &Q,
                       unsigned MaxRecurse, bool IsSigned) {
   // Recursion is always used, so bail out at once if we already hit the limit.
   if (!MaxRecurse--)
     return false;
 
   if (IsSigned) {
-    // TODO: Handle signed.
+    // |X| / |Y| --> 0
+    //
+    // We require that 1 operand is a simple constant. That could be extended to
+    // 2 variables if we computed the sign bit for each.
+    //
+    // Make sure that a constant is not the minimum signed value because taking
+    // the abs() of that is undefined.
+    Type *Ty = X->getType();
+    const APInt *C;
+    if (match(X, m_APInt(C)) && !C->isMinSignedValue()) {
+      // Is the variable divisor magnitude always greater than the constant
+      // dividend magnitude?
+      // |Y| > |C| --> Y < -abs(C) or Y > abs(C)
+      Constant *PosDividendC = ConstantInt::get(Ty, C->abs());
+      Constant *NegDividendC = ConstantInt::get(Ty, -C->abs());
+      if (isICmpTrue(CmpInst::ICMP_SLT, Y, NegDividendC, Q, MaxRecurse) ||
+          isICmpTrue(CmpInst::ICMP_SGT, Y, PosDividendC, Q, MaxRecurse))
+        return true;
+    }
+    if (match(Y, m_APInt(C))) {
+      // Special-case: we can't take the abs() of a minimum signed value. If
+      // that's the divisor, then all we have to do is prove that the dividend
+      // is also not the minimum signed value.
+      if (C->isMinSignedValue())
+        return isICmpTrue(CmpInst::ICMP_NE, X, Y, Q, MaxRecurse);
+
+      // Is the variable dividend magnitude always less than the constant
+      // divisor magnitude?
+      // |X| < |C| --> X > -abs(C) and X < abs(C)
+      Constant *PosDivisorC = ConstantInt::get(Ty, C->abs());
+      Constant *NegDivisorC = ConstantInt::get(Ty, -C->abs());
+      if (isICmpTrue(CmpInst::ICMP_SGT, X, NegDivisorC, Q, MaxRecurse) &&
+          isICmpTrue(CmpInst::ICMP_SLT, X, PosDivisorC, Q, MaxRecurse))
+        return true;
+    }
     return false;
   }
 
   // IsSigned == false.
-  // Is the quotient unsigned less than the divisor?
-  return isICmpTrue(ICmpInst::ICMP_ULT, Op0, Op1, Q, MaxRecurse);
+  // Is the dividend unsigned less than the divisor?
+  return isICmpTrue(ICmpInst::ICMP_ULT, X, Y, Q, MaxRecurse);
 }
 
 /// These are simplifications common to SDiv and UDiv.

Modified: llvm/trunk/test/Transforms/InstCombine/div.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstCombine/div.ll?rev=313264&r1=313263&r2=313264&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/InstCombine/div.ll (original)
+++ llvm/trunk/test/Transforms/InstCombine/div.ll Thu Sep 14 07:59:07 2017
@@ -532,24 +532,21 @@ define i32 @shrink_no(i8 %x) {
   ret i32 %div
 }
 
+; When the divisor is known larger than the quotient,
+; InstSimplify should kill it before InstCombine sees it.
+
 define i32 @shrink_no2(i8 %x) {
 ; CHECK-LABEL: @shrink_no2(
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], -129
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = sext i8 %x to i32
   %div = sdiv i32 %conv, -129
   ret i32 %div
 }
 
-; 17 bits are needed to represent 65535 as a signed value, so this shouldn't fold.
-
 define i32 @shrink_no3(i16 %x) {
 ; CHECK-LABEL: @shrink_no3(
-; CHECK-NEXT:    [[CONV:%.*]] = sext i16 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], 65535
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = sext i16 %x to i32
   %div = sdiv i32 %conv, 65535

Modified: llvm/trunk/test/Transforms/InstSimplify/exact-nsw-nuw.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstSimplify/exact-nsw-nuw.ll?rev=313264&r1=313263&r2=313264&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/InstSimplify/exact-nsw-nuw.ll (original)
+++ llvm/trunk/test/Transforms/InstSimplify/exact-nsw-nuw.ll Thu Sep 14 07:59:07 2017
@@ -60,9 +60,7 @@ define i32 @div1(i32 %V) {
 
 define i32 @div2(i32 %V) {
 ; CHECK-LABEL: @div2(
-; CHECK-NEXT:    [[A:%.*]] = sdiv i32 %V, -1
-; CHECK-NEXT:    [[B:%.*]] = sdiv i32 [[A]], -2147483648
-; CHECK-NEXT:    ret i32 [[B]]
+; CHECK-NEXT:    ret i32 0
 ;
   %A = sdiv i32 %V, -1
   %B = sdiv i32 %A, -2147483648

Modified: llvm/trunk/test/Transforms/InstSimplify/signed-div-rem.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstSimplify/signed-div-rem.ll?rev=313264&r1=313263&r2=313264&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/InstSimplify/signed-div-rem.ll (original)
+++ llvm/trunk/test/Transforms/InstSimplify/signed-div-rem.ll Thu Sep 14 07:59:07 2017
@@ -2,9 +2,7 @@
 
 define i32 @sdiv_sext_big_divisor(i8 %x) {
 ; CHECK-LABEL: @sdiv_sext_big_divisor(
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], 129
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = sext i8 %x to i32
   %div = sdiv i32 %conv, 129
@@ -24,9 +22,7 @@ define i32 @not_sdiv_sext_big_divisor(i8
 
 define i32 @sdiv_sext_small_divisor(i8 %x) {
 ; CHECK-LABEL: @sdiv_sext_small_divisor(
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], -129
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = sext i8 %x to i32
   %div = sdiv i32 %conv, -129
@@ -46,9 +42,7 @@ define i32 @not_sdiv_sext_small_divisor(
 
 define i32 @sdiv_zext_big_divisor(i8 %x) {
 ; CHECK-LABEL: @sdiv_zext_big_divisor(
-; CHECK-NEXT:    [[CONV:%.*]] = zext i8 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], 256
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = zext i8 %x to i32
   %div = sdiv i32 %conv, 256
@@ -68,9 +62,7 @@ define i32 @not_sdiv_zext_big_divisor(i8
 
 define i32 @sdiv_zext_small_divisor(i8 %x) {
 ; CHECK-LABEL: @sdiv_zext_small_divisor(
-; CHECK-NEXT:    [[CONV:%.*]] = zext i8 %x to i32
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[CONV]], -256
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %conv = zext i8 %x to i32
   %div = sdiv i32 %conv, -256
@@ -90,9 +82,7 @@ define i32 @not_sdiv_zext_small_divisor(
 
 define i32 @sdiv_dividend_known_smaller_than_pos_divisor_clear_bits(i32 %x) {
 ; CHECK-LABEL: @sdiv_dividend_known_smaller_than_pos_divisor_clear_bits(
-; CHECK-NEXT:    [[AND:%.*]] = and i32 %x, 253
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[AND]], 254
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %and = and i32 %x, 253
   %div = sdiv i32 %and, 254
@@ -112,9 +102,7 @@ define i32 @not_sdiv_dividend_known_smal
 
 define i32 @sdiv_dividend_known_smaller_than_neg_divisor_clear_bits(i32 %x) {
 ; CHECK-LABEL: @sdiv_dividend_known_smaller_than_neg_divisor_clear_bits(
-; CHECK-NEXT:    [[AND:%.*]] = and i32 %x, 253
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[AND]], -254
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %and = and i32 %x, 253
   %div = sdiv i32 %and, -254
@@ -134,9 +122,7 @@ define i32 @not_sdiv_dividend_known_smal
 
 define i32 @sdiv_dividend_known_smaller_than_pos_divisor_set_bits(i32 %x) {
 ; CHECK-LABEL: @sdiv_dividend_known_smaller_than_pos_divisor_set_bits(
-; CHECK-NEXT:    [[OR:%.*]] = or i32 %x, -253
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[OR]], 254
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %or = or i32 %x, -253
   %div = sdiv i32 %or, 254
@@ -156,9 +142,7 @@ define i32 @not_sdiv_dividend_known_smal
 
 define i32 @sdiv_dividend_known_smaller_than_neg_divisor_set_bits(i32 %x) {
 ; CHECK-LABEL: @sdiv_dividend_known_smaller_than_neg_divisor_set_bits(
-; CHECK-NEXT:    [[OR:%.*]] = or i32 %x, -253
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[OR]], -254
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 0
 ;
   %or = or i32 %x, -253
   %div = sdiv i32 %or, -254
@@ -179,8 +163,7 @@ define i32 @not_sdiv_dividend_known_smal
 define i32 @srem_sext_big_divisor(i8 %x) {
 ; CHECK-LABEL: @srem_sext_big_divisor(
 ; CHECK-NEXT:    [[CONV:%.*]] = sext i8 %x to i32
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[CONV]], 129
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[CONV]]
 ;
   %conv = sext i8 %x to i32
   %rem = srem i32 %conv, 129
@@ -201,8 +184,7 @@ define i32 @not_srem_sext_big_divisor(i8
 define i32 @srem_sext_small_divisor(i8 %x) {
 ; CHECK-LABEL: @srem_sext_small_divisor(
 ; CHECK-NEXT:    [[CONV:%.*]] = sext i8 %x to i32
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[CONV]], -129
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[CONV]]
 ;
   %conv = sext i8 %x to i32
   %rem = srem i32 %conv, -129
@@ -223,8 +205,7 @@ define i32 @not_srem_sext_small_divisor(
 define i32 @srem_zext_big_divisor(i8 %x) {
 ; CHECK-LABEL: @srem_zext_big_divisor(
 ; CHECK-NEXT:    [[CONV:%.*]] = zext i8 %x to i32
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[CONV]], 256
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[CONV]]
 ;
   %conv = zext i8 %x to i32
   %rem = srem i32 %conv, 256
@@ -245,8 +226,7 @@ define i32 @not_srem_zext_big_divisor(i8
 define i32 @srem_zext_small_divisor(i8 %x) {
 ; CHECK-LABEL: @srem_zext_small_divisor(
 ; CHECK-NEXT:    [[CONV:%.*]] = zext i8 %x to i32
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[CONV]], -256
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[CONV]]
 ;
   %conv = zext i8 %x to i32
   %rem = srem i32 %conv, -256
@@ -267,8 +247,7 @@ define i32 @not_srem_zext_small_divisor(
 define i32 @srem_dividend_known_smaller_than_pos_divisor_clear_bits(i32 %x) {
 ; CHECK-LABEL: @srem_dividend_known_smaller_than_pos_divisor_clear_bits(
 ; CHECK-NEXT:    [[AND:%.*]] = and i32 %x, 253
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[AND]], 254
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[AND]]
 ;
   %and = and i32 %x, 253
   %rem = srem i32 %and, 254
@@ -289,8 +268,7 @@ define i32 @not_srem_dividend_known_smal
 define i32 @srem_dividend_known_smaller_than_neg_divisor_clear_bits(i32 %x) {
 ; CHECK-LABEL: @srem_dividend_known_smaller_than_neg_divisor_clear_bits(
 ; CHECK-NEXT:    [[AND:%.*]] = and i32 %x, 253
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[AND]], -254
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[AND]]
 ;
   %and = and i32 %x, 253
   %rem = srem i32 %and, -254
@@ -311,8 +289,7 @@ define i32 @not_srem_dividend_known_smal
 define i32 @srem_dividend_known_smaller_than_pos_divisor_set_bits(i32 %x) {
 ; CHECK-LABEL: @srem_dividend_known_smaller_than_pos_divisor_set_bits(
 ; CHECK-NEXT:    [[OR:%.*]] = or i32 %x, -253
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[OR]], 254
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %or = or i32 %x, -253
   %rem = srem i32 %or, 254
@@ -333,8 +310,7 @@ define i32 @not_srem_dividend_known_smal
 define i32 @srem_dividend_known_smaller_than_neg_divisor_set_bits(i32 %x) {
 ; CHECK-LABEL: @srem_dividend_known_smaller_than_neg_divisor_set_bits(
 ; CHECK-NEXT:    [[OR:%.*]] = or i32 %x, -253
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[OR]], -254
-; CHECK-NEXT:    ret i32 [[REM]]
+; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %or = or i32 %x, -253
   %rem = srem i32 %or, -254
@@ -352,3 +328,27 @@ define i32 @not_srem_dividend_known_smal
   ret i32 %rem
 }
 
+; Make sure that we're handling the minimum signed constant correctly - can't fold this.
+
+define i16 @sdiv_min_dividend(i8 %x) {
+; CHECK-LABEL: @sdiv_min_dividend(
+; CHECK-NEXT:    [[Z:%.*]] = zext i8 %x to i16
+; CHECK-NEXT:    [[D:%.*]] = sdiv i16 -32768, [[Z]]
+; CHECK-NEXT:    ret i16 [[D]]
+;
+  %z = zext i8 %x to i16
+  %d = sdiv i16 -32768, %z
+  ret i16 %d
+}
+
+; If the quotient is known to not be -32768, then this can fold.
+
+define i16 @sdiv_min_divisor(i8 %x) {
+; CHECK-LABEL: @sdiv_min_divisor(
+; CHECK-NEXT:    ret i16 0
+;
+  %z = zext i8 %x to i16
+  %d = sdiv i16 %z, -32768
+  ret i16 %d
+}
+




More information about the llvm-commits mailing list