[llvm] r235699 - Fix APInt long division algorithm

Pawel Bylica chfast at gmail.com
Fri Apr 24 00:38:40 PDT 2015


Author: chfast
Date: Fri Apr 24 02:38:39 2015
New Revision: 235699

URL: http://llvm.org/viewvc/llvm-project?rev=235699&view=rev
Log:
Fix APInt long division algorithm

Summary: This patch fixes step D4 of Knuth's division algorithm implementation. Negative sign of the step result was not always detected due to incorrect "borrow" handling.

Test Plan: Unit test that reveals the bug included.

Reviewers: chandlerc, yaron.keren

Reviewed By: yaron.keren

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D9196

Modified:
    llvm/trunk/lib/Support/APInt.cpp
    llvm/trunk/unittests/ADT/APIntTest.cpp

Modified: llvm/trunk/lib/Support/APInt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/APInt.cpp?rev=235699&r1=235698&r2=235699&view=diff
==============================================================================
--- llvm/trunk/lib/Support/APInt.cpp (original)
+++ llvm/trunk/lib/Support/APInt.cpp Fri Apr 24 02:38:39 2015
@@ -1586,28 +1586,18 @@ static void KnuthDiv(unsigned *u, unsign
     // this step is actually negative, (u[j+n]...u[j]) should be left as the
     // true value plus b**(n+1), namely as the b's complement of
     // the true value, and a "borrow" to the left should be remembered.
-    bool isNeg = false;
+    int64_t borrow = 0;
     for (unsigned i = 0; i < n; ++i) {
-      uint64_t u_tmp = (uint64_t(u[j+i+1]) << 32) | uint64_t(u[j+i]);
-      uint64_t subtrahend = uint64_t(qp) * uint64_t(v[i]);
-      bool borrow = subtrahend > u_tmp;
-      DEBUG(dbgs() << "KnuthDiv: u_tmp = " << u_tmp
-                   << ", subtrahend = " << subtrahend
+      uint64_t p = uint64_t(qp) * uint64_t(v[i]);
+      int64_t subres = int64_t(u[j+i]) - borrow - (unsigned)p;
+      u[j+i] = (unsigned)subres;
+      borrow = (p >> 32) - (subres >> 32);
+      DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j+i]
                    << ", borrow = " << borrow << '\n');
-
-      uint64_t result = u_tmp - subtrahend;
-      unsigned k = j + i;
-      u[k++] = (unsigned)result;         // subtraction low word
-      u[k++] = (unsigned)(result >> 32); // subtraction high word
-      while (borrow && k <= m+n) {       // deal with borrow to the left
-        borrow = u[k] == 0;
-        u[k]--;
-        k++;
-      }
-      isNeg |= borrow;
-      DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j+i] 
-                   << ", u[j+i+1] = " << u[j+i+1] << '\n');
     }
+    bool isNeg = u[j+n] < borrow;
+    u[j+n] -= (unsigned)borrow;
+
     DEBUG(dbgs() << "KnuthDiv: after subtraction:");
     DEBUG(for (int i = m+n; i >=0; i--) dbgs() << " " << u[i]);
     DEBUG(dbgs() << '\n');

Modified: llvm/trunk/unittests/ADT/APIntTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/APIntTest.cpp?rev=235699&r1=235698&r2=235699&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/APIntTest.cpp (original)
+++ llvm/trunk/unittests/ADT/APIntTest.cpp Fri Apr 24 02:38:39 2015
@@ -209,217 +209,101 @@ TEST(APIntTest, i1) {
   }
 }
 
-TEST(APIntTest, divrem_big1) {
-  // Tests KnuthDiv rare step D6
-  APInt a{256, "1ffffffffffffffff", 16};
-  APInt b{256, "1ffffffffffffffff", 16};
-  APInt c{256, 0};
+
+// Tests different div/rem varaints using scheme (a * b + c) / a
+void testDiv(APInt a, APInt b, APInt c) {
+  ASSERT_TRUE(a.uge(b)); // Must: a >= b
+  ASSERT_TRUE(a.ugt(c)); // Must: a > c
 
   auto p = a * b + c;
+
   auto q = p.udiv(a);
   auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
   APInt::udivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.udiv(b);
-  r = p.urem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
   q = p.sdiv(a);
   r = p.srem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
   APInt::sdivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(b);
-  r = p.srem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
+
+  if (b.ugt(c)) { // Test also symmetric case
+    q = p.udiv(b);
+    r = p.urem(b);
+    EXPECT_EQ(a, q);
+    EXPECT_EQ(c, r);
+    APInt::udivrem(p, b, q, r);
+    EXPECT_EQ(a, q);
+    EXPECT_EQ(c, r);
+    q = p.sdiv(b);
+    r = p.srem(b);
+    EXPECT_EQ(a, q);
+    EXPECT_EQ(c, r);
+    APInt::sdivrem(p, b, q, r);
+    EXPECT_EQ(a, q);
+    EXPECT_EQ(c, r);
+  }
 }
 
-TEST(APIntTest, divrem_big2) {
+TEST(APIntTest, divrem_big1) {
   // Tests KnuthDiv rare step D6
-  APInt a{1024,           "111111ffffffffffffffff"
-                "ffffffffffffffffffffffffffffffff"
-                "fffffffffffffffffffffffffffffccf"
-                "ffffffffffffffffffffffffffffff00", 16};
-  APInt b{1024,                       "112233ceff"
-                "cecece000000ffffffffffffffffffff"
-                "ffffffffffffffffffffffffffffffff"
-                "ffffffffffffffffffffffffffffffff"
-                "ffffffffffffffffffffffffffffff33", 16};
-  APInt c{1024, 7919};
+  testDiv({256, "1ffffffffffffffff", 16},
+          {256, "1ffffffffffffffff", 16},
+          {256, 0});
+}
 
-  auto p = a * b + c;
-  auto q = p.udiv(a);
-  auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.udiv(b);
-  r = p.urem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(a);
-  r = p.srem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(b);
-  r = p.srem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+TEST(APIntTest, divrem_big2) {
+  // Tests KnuthDiv rare step D6
+  testDiv({1024,                       "112233ceff"
+                 "cecece000000ffffffffffffffffffff"
+                 "ffffffffffffffffffffffffffffffff"
+                 "ffffffffffffffffffffffffffffffff"
+                 "ffffffffffffffffffffffffffffff33", 16},
+          {1024,           "111111ffffffffffffffff"
+                 "ffffffffffffffffffffffffffffffff"
+                 "fffffffffffffffffffffffffffffccf"
+                 "ffffffffffffffffffffffffffffff00", 16},
+          {1024, 7919});
 }
 
 TEST(APIntTest, divrem_big3) {
   // Tests KnuthDiv case without shift
-  APInt a{256, "ffffffffffffff0000000", 16};
-  APInt b{256, "80000001ffffffffffffffff", 16};
-  APInt c{256, 4219};
-
-  auto p = a * b + c;
-  auto q = p.udiv(a);
-  auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.udiv(b);
-  r = p.urem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(a);
-  r = p.srem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(b);
-  r = p.srem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+  testDiv({256, "80000001ffffffffffffffff", 16},
+          {256, "ffffffffffffff0000000", 16},
+          {256, 4219});
 }
 
 TEST(APIntTest, divrem_big4) {
   // Tests heap allocation in divide() enfoced by huge numbers
-  auto a = APInt{4096, 1}.shl(2000);
-  auto b = APInt{4096, 5}.shl(2001);
-  auto c = APInt{4096, 4219*13};
-
-  auto p = a * b + c;
-  auto q = p.udiv(a);
-  auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = APInt{1024, 0}; // test non-single word APInt conversion in divide()
-  r = APInt{1024, 0};
-  APInt::udivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.udiv(b);
-  r = p.urem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = APInt{1024, 0};
-  r = APInt{1024, 0};
-  APInt::udivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(a);
-  r = p.srem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = APInt{1024, 0};
-  r = APInt{1024, 0};
-  APInt::sdivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(b);
-  r = p.srem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = APInt{1024, 0};
-  r = APInt{1024, 0};
-  APInt::sdivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+  testDiv(APInt{4096, 5}.shl(2001),
+          APInt{4096, 1}.shl(2000),
+          APInt{4096, 4219*13});
 }
 
 TEST(APIntTest, divrem_big5) {
   // Tests one word divisor case of divide()
-  auto a = APInt{1024, 19}.shl(811);
-  auto b = APInt{1024, 4356013}; // one word
-  auto c = APInt{1024, 1};
+  testDiv(APInt{1024, 19}.shl(811),
+          APInt{1024, 4356013}, // one word
+          APInt{1024, 1});
+}
 
-  auto p = a * b + c;
-  auto q = p.udiv(a);
-  auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.udiv(b);
-  r = p.urem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::udivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(a);
-  r = p.srem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, a, q, r);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
-  q = p.sdiv(b);
-  r = p.srem(b);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
-  APInt::sdivrem(p, b, q, r);
-  EXPECT_EQ(q, a);
-  EXPECT_EQ(r, c);
+TEST(APIntTest, divrem_big6) {
+  // Tests some rare "borrow" cases in D4 step
+  testDiv(APInt{512, "ffffffffffffffff00000000000000000000000001", 16},
+          APInt{512, "10000000000000001000000000000001", 16},
+          APInt{512, "10000000000000000000000000000000", 16});
 }
 
 TEST(APIntTest, divrem_big7) {
   // Yet another test for KnuthDiv rare step D6.
-  APInt a{224, "800000008000000200000005", 16};
-  APInt b{224, "fffffffd", 16};
-  APInt c{224, "80000000800000010000000f", 16};
-
-  auto p = a * b + c;
-  auto q = p.udiv(a);
-  auto r = p.urem(a);
-  EXPECT_EQ(q, b);
-  EXPECT_EQ(r, c);
+  testDiv({224, "800000008000000200000005", 16},
+          {224, "fffffffd", 16},
+          {224, "80000000800000010000000f", 16});
 }
 
 TEST(APIntTest, fromString) {





More information about the llvm-commits mailing list