mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-21 21:29:41 +00:00
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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@235699 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
a9a01d9879
commit
9ee9f1a334
@ -1586,28 +1586,18 @@ static void KnuthDiv(unsigned *u, unsigned *v, unsigned *q, unsigned* r,
|
|||||||
// this step is actually negative, (u[j+n]...u[j]) should be left as the
|
// 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
|
// 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.
|
// 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) {
|
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 p = uint64_t(qp) * uint64_t(v[i]);
|
||||||
uint64_t subtrahend = uint64_t(qp) * uint64_t(v[i]);
|
int64_t subres = int64_t(u[j+i]) - borrow - (unsigned)p;
|
||||||
bool borrow = subtrahend > u_tmp;
|
u[j+i] = (unsigned)subres;
|
||||||
DEBUG(dbgs() << "KnuthDiv: u_tmp = " << u_tmp
|
borrow = (p >> 32) - (subres >> 32);
|
||||||
<< ", subtrahend = " << subtrahend
|
DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j+i]
|
||||||
<< ", borrow = " << borrow << '\n');
|
<< ", 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(dbgs() << "KnuthDiv: after subtraction:");
|
||||||
DEBUG(for (int i = m+n; i >=0; i--) dbgs() << " " << u[i]);
|
DEBUG(for (int i = m+n; i >=0; i--) dbgs() << " " << u[i]);
|
||||||
DEBUG(dbgs() << '\n');
|
DEBUG(dbgs() << '\n');
|
||||||
|
@ -209,217 +209,101 @@ TEST(APIntTest, i1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(APIntTest, divrem_big1) {
|
|
||||||
// Tests KnuthDiv rare step D6
|
// Tests different div/rem varaints using scheme (a * b + c) / a
|
||||||
APInt a{256, "1ffffffffffffffff", 16};
|
void testDiv(APInt a, APInt b, APInt c) {
|
||||||
APInt b{256, "1ffffffffffffffff", 16};
|
ASSERT_TRUE(a.uge(b)); // Must: a >= b
|
||||||
APInt c{256, 0};
|
ASSERT_TRUE(a.ugt(c)); // Must: a > c
|
||||||
|
|
||||||
auto p = a * b + c;
|
auto p = a * b + c;
|
||||||
|
|
||||||
auto q = p.udiv(a);
|
auto q = p.udiv(a);
|
||||||
auto r = p.urem(a);
|
auto r = p.urem(a);
|
||||||
EXPECT_EQ(q, b);
|
EXPECT_EQ(b, q);
|
||||||
EXPECT_EQ(r, c);
|
EXPECT_EQ(c, r);
|
||||||
APInt::udivrem(p, a, q, r);
|
APInt::udivrem(p, a, q, r);
|
||||||
EXPECT_EQ(q, b);
|
EXPECT_EQ(b, q);
|
||||||
EXPECT_EQ(r, c);
|
EXPECT_EQ(c, r);
|
||||||
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);
|
q = p.sdiv(a);
|
||||||
r = p.srem(a);
|
r = p.srem(a);
|
||||||
EXPECT_EQ(q, b);
|
EXPECT_EQ(b, q);
|
||||||
EXPECT_EQ(r, c);
|
EXPECT_EQ(c, r);
|
||||||
APInt::sdivrem(p, a, q, r);
|
APInt::sdivrem(p, a, q, r);
|
||||||
EXPECT_EQ(q, b);
|
EXPECT_EQ(b, q);
|
||||||
EXPECT_EQ(r, c);
|
EXPECT_EQ(c, r);
|
||||||
q = p.sdiv(b);
|
|
||||||
r = p.srem(b);
|
if (b.ugt(c)) { // Test also symmetric case
|
||||||
EXPECT_EQ(q, a);
|
q = p.udiv(b);
|
||||||
EXPECT_EQ(r, c);
|
r = p.urem(b);
|
||||||
APInt::sdivrem(p, b, q, r);
|
EXPECT_EQ(a, q);
|
||||||
EXPECT_EQ(q, a);
|
EXPECT_EQ(c, r);
|
||||||
EXPECT_EQ(r, c);
|
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_big1) {
|
||||||
|
// Tests KnuthDiv rare step D6
|
||||||
|
testDiv({256, "1ffffffffffffffff", 16},
|
||||||
|
{256, "1ffffffffffffffff", 16},
|
||||||
|
{256, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(APIntTest, divrem_big2) {
|
TEST(APIntTest, divrem_big2) {
|
||||||
// Tests KnuthDiv rare step D6
|
// Tests KnuthDiv rare step D6
|
||||||
APInt a{1024, "111111ffffffffffffffff"
|
testDiv({1024, "112233ceff"
|
||||||
"ffffffffffffffffffffffffffffffff"
|
"cecece000000ffffffffffffffffffff"
|
||||||
"fffffffffffffffffffffffffffffccf"
|
"ffffffffffffffffffffffffffffffff"
|
||||||
"ffffffffffffffffffffffffffffff00", 16};
|
"ffffffffffffffffffffffffffffffff"
|
||||||
APInt b{1024, "112233ceff"
|
"ffffffffffffffffffffffffffffff33", 16},
|
||||||
"cecece000000ffffffffffffffffffff"
|
{1024, "111111ffffffffffffffff"
|
||||||
"ffffffffffffffffffffffffffffffff"
|
"ffffffffffffffffffffffffffffffff"
|
||||||
"ffffffffffffffffffffffffffffffff"
|
"fffffffffffffffffffffffffffffccf"
|
||||||
"ffffffffffffffffffffffffffffff33", 16};
|
"ffffffffffffffffffffffffffffff00", 16},
|
||||||
APInt c{1024, 7919};
|
{1024, 7919});
|
||||||
|
|
||||||
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_big3) {
|
TEST(APIntTest, divrem_big3) {
|
||||||
// Tests KnuthDiv case without shift
|
// Tests KnuthDiv case without shift
|
||||||
APInt a{256, "ffffffffffffff0000000", 16};
|
testDiv({256, "80000001ffffffffffffffff", 16},
|
||||||
APInt b{256, "80000001ffffffffffffffff", 16};
|
{256, "ffffffffffffff0000000", 16},
|
||||||
APInt c{256, 4219};
|
{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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(APIntTest, divrem_big4) {
|
TEST(APIntTest, divrem_big4) {
|
||||||
// Tests heap allocation in divide() enfoced by huge numbers
|
// Tests heap allocation in divide() enfoced by huge numbers
|
||||||
auto a = APInt{4096, 1}.shl(2000);
|
testDiv(APInt{4096, 5}.shl(2001),
|
||||||
auto b = APInt{4096, 5}.shl(2001);
|
APInt{4096, 1}.shl(2000),
|
||||||
auto c = APInt{4096, 4219*13};
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(APIntTest, divrem_big5) {
|
TEST(APIntTest, divrem_big5) {
|
||||||
// Tests one word divisor case of divide()
|
// Tests one word divisor case of divide()
|
||||||
auto a = APInt{1024, 19}.shl(811);
|
testDiv(APInt{1024, 19}.shl(811),
|
||||||
auto b = APInt{1024, 4356013}; // one word
|
APInt{1024, 4356013}, // one word
|
||||||
auto c = APInt{1024, 1};
|
APInt{1024, 1});
|
||||||
|
}
|
||||||
|
|
||||||
auto p = a * b + c;
|
TEST(APIntTest, divrem_big6) {
|
||||||
auto q = p.udiv(a);
|
// Tests some rare "borrow" cases in D4 step
|
||||||
auto r = p.urem(a);
|
testDiv(APInt{512, "ffffffffffffffff00000000000000000000000001", 16},
|
||||||
EXPECT_EQ(q, b);
|
APInt{512, "10000000000000001000000000000001", 16},
|
||||||
EXPECT_EQ(r, c);
|
APInt{512, "10000000000000000000000000000000", 16});
|
||||||
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_big7) {
|
TEST(APIntTest, divrem_big7) {
|
||||||
// Yet another test for KnuthDiv rare step D6.
|
// Yet another test for KnuthDiv rare step D6.
|
||||||
APInt a{224, "800000008000000200000005", 16};
|
testDiv({224, "800000008000000200000005", 16},
|
||||||
APInt b{224, "fffffffd", 16};
|
{224, "fffffffd", 16},
|
||||||
APInt c{224, "80000000800000010000000f", 16};
|
{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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(APIntTest, fromString) {
|
TEST(APIntTest, fromString) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user