[bigint] Move division to src/bigint/
No changes to the algorithm; minor speedup due to the move from Handle<BigInt> to Digits. Bug: v8:11515 Change-Id: Id85fe4f0c276d3ad826fee79205719092d0e0715 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2947412 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#75158}
This commit is contained in:
parent
1808ba9718
commit
ca29ff4393
3
BUILD.gn
3
BUILD.gn
@ -4927,6 +4927,9 @@ v8_source_set("v8_bigint") {
|
||||
"src/bigint/bigint-internal.h",
|
||||
"src/bigint/bigint.h",
|
||||
"src/bigint/digit-arithmetic.h",
|
||||
"src/bigint/div-helpers.cc",
|
||||
"src/bigint/div-helpers.h",
|
||||
"src/bigint/div-schoolbook.cc",
|
||||
"src/bigint/mul-karatsuba.cc",
|
||||
"src/bigint/mul-schoolbook.cc",
|
||||
"src/bigint/util.h",
|
||||
|
@ -34,11 +34,62 @@ void ProcessorImpl::Multiply(RWDigits Z, Digits X, Digits Y) {
|
||||
return MultiplyKaratsuba(Z, X, Y);
|
||||
}
|
||||
|
||||
void ProcessorImpl::Divide(RWDigits Q, Digits A, Digits B) {
|
||||
A.Normalize();
|
||||
B.Normalize();
|
||||
DCHECK(B.len() > 0); // NOLINT(readability/check)
|
||||
int cmp = Compare(A, B);
|
||||
if (cmp < 0) return Q.Clear();
|
||||
if (cmp == 0) {
|
||||
Q[0] = 1;
|
||||
for (int i = 1; i < Q.len(); i++) Q[i] = 0;
|
||||
return;
|
||||
}
|
||||
if (B.len() == 1) {
|
||||
digit_t remainder;
|
||||
return DivideSingle(Q, &remainder, A, B[0]);
|
||||
}
|
||||
return DivideSchoolbook(Q, RWDigits(nullptr, 0), A, B);
|
||||
}
|
||||
|
||||
void ProcessorImpl::Modulo(RWDigits R, Digits A, Digits B) {
|
||||
A.Normalize();
|
||||
B.Normalize();
|
||||
DCHECK(B.len() > 0); // NOLINT(readability/check)
|
||||
int cmp = Compare(A, B);
|
||||
if (cmp < 0) {
|
||||
for (int i = 0; i < B.len(); i++) R[i] = B[i];
|
||||
for (int i = B.len(); i < R.len(); i++) R[i] = 0;
|
||||
return;
|
||||
}
|
||||
if (cmp == 0) return R.Clear();
|
||||
if (B.len() == 1) {
|
||||
digit_t remainder;
|
||||
DivideSingle(RWDigits(nullptr, 0), &remainder, A, B[0]);
|
||||
R[0] = remainder;
|
||||
for (int i = 1; i < R.len(); i++) R[i] = 0;
|
||||
return;
|
||||
}
|
||||
return DivideSchoolbook(RWDigits(nullptr, 0), R, A, B);
|
||||
}
|
||||
|
||||
Status Processor::Multiply(RWDigits Z, Digits X, Digits Y) {
|
||||
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
|
||||
impl->Multiply(Z, X, Y);
|
||||
return impl->get_and_clear_status();
|
||||
}
|
||||
|
||||
Status Processor::Divide(RWDigits Q, Digits A, Digits B) {
|
||||
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
|
||||
impl->Divide(Q, A, B);
|
||||
return impl->get_and_clear_status();
|
||||
}
|
||||
|
||||
Status Processor::Modulo(RWDigits R, Digits A, Digits B) {
|
||||
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
|
||||
impl->Modulo(R, A, B);
|
||||
return impl->get_and_clear_status();
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
@ -30,6 +30,12 @@ class ProcessorImpl : public Processor {
|
||||
void KaratsubaChunk(RWDigits Z, Digits X, Digits Y, RWDigits scratch);
|
||||
void KaratsubaMain(RWDigits Z, Digits X, Digits Y, RWDigits scratch, int n);
|
||||
|
||||
void Divide(RWDigits Q, Digits A, Digits B);
|
||||
void DivideSingle(RWDigits Q, digit_t* remainder, Digits A, digit_t b);
|
||||
void DivideSchoolbook(RWDigits Q, RWDigits R, Digits A, Digits B);
|
||||
|
||||
void Modulo(RWDigits R, Digits A, Digits B);
|
||||
|
||||
private:
|
||||
// Each unit is supposed to represent approximately one CPU {mul} instruction.
|
||||
// Doesn't need to be accurate; we just want to make sure to check for
|
||||
|
@ -101,7 +101,7 @@ class Digits {
|
||||
const digit_t* digits() const { return digits_; }
|
||||
|
||||
protected:
|
||||
friend class TemporaryLeftShift;
|
||||
friend class ShiftedDigits;
|
||||
digit_t* digits_;
|
||||
int len_;
|
||||
|
||||
@ -201,7 +201,7 @@ class Platform {
|
||||
//
|
||||
// The operations are divided into two groups: "fast" (O(n) with small
|
||||
// coefficient) operations are exposed directly as free functions, "slow"
|
||||
// operations are methods on a {BigIntProcessor} object, which provides
|
||||
// operations are methods on a {Processor} object, which provides
|
||||
// support for interrupting execution via the {Platform}'s {InterruptRequested}
|
||||
// mechanism when it takes too long. These functions return a {Status} value.
|
||||
|
||||
@ -226,7 +226,7 @@ class Processor {
|
||||
// Takes ownership of {platform}.
|
||||
static Processor* New(Platform* platform);
|
||||
|
||||
// Use this for any std::unique_ptr holding an instance of BigIntProcessor.
|
||||
// Use this for any std::unique_ptr holding an instance of {Processor}.
|
||||
class Destroyer {
|
||||
public:
|
||||
void operator()(Processor* proc) { proc->Destroy(); }
|
||||
@ -236,11 +236,19 @@ class Processor {
|
||||
|
||||
// Z := X * Y
|
||||
Status Multiply(RWDigits Z, Digits X, Digits Y);
|
||||
// Q := A / B
|
||||
Status Divide(RWDigits Q, Digits A, Digits B);
|
||||
// R := A % B
|
||||
Status Modulo(RWDigits R, Digits A, Digits B);
|
||||
};
|
||||
|
||||
inline int MultiplyResultLength(Digits X, Digits Y) {
|
||||
return X.len() + Y.len();
|
||||
}
|
||||
inline int DivideResultLength(Digits A, Digits B) {
|
||||
return A.len() - B.len() + 1;
|
||||
}
|
||||
inline int ModuloResultLength(Digits B) { return B.len(); }
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define V8_BIGINT_DIGIT_ARITHMETIC_H_
|
||||
|
||||
#include "src/bigint/bigint.h"
|
||||
#include "src/bigint/util.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
@ -111,6 +112,81 @@ inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the quotient.
|
||||
// quotient = (high << kDigitBits + low - remainder) / divisor
|
||||
static inline digit_t digit_div(digit_t high, digit_t low, digit_t divisor,
|
||||
digit_t* remainder) {
|
||||
#if defined(DCHECK)
|
||||
DCHECK(high < divisor);
|
||||
DCHECK(divisor != 0); // NOLINT(readability/check)
|
||||
#endif
|
||||
#if __x86_64__ && (__GNUC__ || __clang__)
|
||||
digit_t quotient;
|
||||
digit_t rem;
|
||||
__asm__("divq %[divisor]"
|
||||
// Outputs: {quotient} will be in rax, {rem} in rdx.
|
||||
: "=a"(quotient), "=d"(rem)
|
||||
// Inputs: put {high} into rdx, {low} into rax, and {divisor} into
|
||||
// any register or stack slot.
|
||||
: "d"(high), "a"(low), [divisor] "rm"(divisor));
|
||||
*remainder = rem;
|
||||
return quotient;
|
||||
#elif __i386__ && (__GNUC__ || __clang__)
|
||||
digit_t quotient;
|
||||
digit_t rem;
|
||||
__asm__("divl %[divisor]"
|
||||
// Outputs: {quotient} will be in eax, {rem} in edx.
|
||||
: "=a"(quotient), "=d"(rem)
|
||||
// Inputs: put {high} into edx, {low} into eax, and {divisor} into
|
||||
// any register or stack slot.
|
||||
: "d"(high), "a"(low), [divisor] "rm"(divisor));
|
||||
*remainder = rem;
|
||||
return quotient;
|
||||
#else
|
||||
// Adapted from Warren, Hacker's Delight, p. 152.
|
||||
int s = CountLeadingZeros(divisor);
|
||||
DCHECK(s != kDigitBits); // {divisor} is not 0.
|
||||
divisor <<= s;
|
||||
|
||||
digit_t vn1 = divisor >> kHalfDigitBits;
|
||||
digit_t vn0 = divisor & kHalfDigitMask;
|
||||
// {s} can be 0. {low >> kDigitBits} would be undefined behavior, so
|
||||
// we mask the shift amount with {kShiftMask}, and the result with
|
||||
// {s_zero_mask} which is 0 if s == 0 and all 1-bits otherwise.
|
||||
static_assert(sizeof(intptr_t) == sizeof(digit_t),
|
||||
"intptr_t and digit_t must have the same size");
|
||||
const int kShiftMask = kDigitBits - 1;
|
||||
digit_t s_zero_mask =
|
||||
static_cast<digit_t>(static_cast<intptr_t>(-s) >> (kDigitBits - 1));
|
||||
digit_t un32 =
|
||||
(high << s) | ((low >> ((kDigitBits - s) & kShiftMask)) & s_zero_mask);
|
||||
digit_t un10 = low << s;
|
||||
digit_t un1 = un10 >> kHalfDigitBits;
|
||||
digit_t un0 = un10 & kHalfDigitMask;
|
||||
digit_t q1 = un32 / vn1;
|
||||
digit_t rhat = un32 - q1 * vn1;
|
||||
|
||||
while (q1 >= kHalfDigitBase || q1 * vn0 > rhat * kHalfDigitBase + un1) {
|
||||
q1--;
|
||||
rhat += vn1;
|
||||
if (rhat >= kHalfDigitBase) break;
|
||||
}
|
||||
|
||||
digit_t un21 = un32 * kHalfDigitBase + un1 - q1 * divisor;
|
||||
digit_t q0 = un21 / vn1;
|
||||
rhat = un21 - q0 * vn1;
|
||||
|
||||
while (q0 >= kHalfDigitBase || q0 * vn0 > rhat * kHalfDigitBase + un0) {
|
||||
q0--;
|
||||
rhat += vn1;
|
||||
if (rhat >= kHalfDigitBase) break;
|
||||
}
|
||||
|
||||
*remainder = (un21 * kHalfDigitBase + un0 - q0 * divisor) >> s;
|
||||
return q1 * kHalfDigitBase + q0;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
||||
|
68
src/bigint/div-helpers.cc
Normal file
68
src/bigint/div-helpers.cc
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/bigint/div-helpers.h"
|
||||
|
||||
#include "src/bigint/bigint-internal.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
|
||||
namespace {
|
||||
|
||||
void Copy(RWDigits Z, Digits X) {
|
||||
if (Z == X) return;
|
||||
int i = 0;
|
||||
for (; i < X.len(); i++) Z[i] = X[i];
|
||||
for (; i < Z.len(); i++) Z[i] = 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Z := X << shift
|
||||
// Z and X may alias for an in-place shift.
|
||||
void LeftShift(RWDigits Z, Digits X, int shift) {
|
||||
DCHECK(shift >= 0); // NOLINT(readability/check)
|
||||
DCHECK(shift < kDigitBits);
|
||||
DCHECK(Z.len() >= X.len());
|
||||
if (shift == 0) return Copy(Z, X);
|
||||
digit_t carry = 0;
|
||||
int i = 0;
|
||||
for (; i < X.len(); i++) {
|
||||
digit_t d = X[i];
|
||||
Z[i] = (d << shift) | carry;
|
||||
carry = d >> (kDigitBits - shift);
|
||||
}
|
||||
if (i < Z.len()) {
|
||||
Z[i++] = carry;
|
||||
} else {
|
||||
DCHECK(carry == 0); // NOLINT(readability/check)
|
||||
}
|
||||
for (; i < Z.len(); i++) Z[i] = 0;
|
||||
}
|
||||
|
||||
// Z := X >> shift
|
||||
// Z and X may alias for an in-place shift.
|
||||
void RightShift(RWDigits Z, Digits X, int shift) {
|
||||
DCHECK(shift >= 0); // NOLINT(readability/check)
|
||||
DCHECK(shift < kDigitBits);
|
||||
X.Normalize();
|
||||
DCHECK(Z.len() >= X.len());
|
||||
if (shift == 0) return Copy(Z, X);
|
||||
int i = 0;
|
||||
if (X.len() > 0) {
|
||||
digit_t carry = X[0] >> shift;
|
||||
int last = X.len() - 1;
|
||||
for (; i < last; i++) {
|
||||
digit_t d = X[i + 1];
|
||||
Z[i] = (d << (kDigitBits - shift)) | carry;
|
||||
carry = d >> shift;
|
||||
}
|
||||
Z[i++] = carry;
|
||||
}
|
||||
for (; i < Z.len(); i++) Z[i] = 0;
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
75
src/bigint/div-helpers.h
Normal file
75
src/bigint/div-helpers.h
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_BIGINT_DIV_HELPERS_H_
|
||||
#define V8_BIGINT_DIV_HELPERS_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/bigint/bigint.h"
|
||||
#include "src/bigint/util.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
|
||||
void LeftShift(RWDigits Z, Digits X, int shift);
|
||||
void RightShift(RWDigits Z, Digits X, int shift);
|
||||
|
||||
// Division algorithms typically need to left-shift their inputs into
|
||||
// "bit-normalized" form (i.e. top bit is set). The inputs are considered
|
||||
// read-only, and V8 relies on that by allowing concurrent reads from them,
|
||||
// so by default, {ShiftedDigits} allocate temporary storage for their
|
||||
// contents. In-place modification is opt-in for cases where callers can
|
||||
// guarantee that it is safe.
|
||||
// When callers allow in-place shifting and wish to undo it, they have to do
|
||||
// so manually using {Reset()}.
|
||||
// If {shift} is not given, it is auto-detected from {original}'s
|
||||
// leading zeros.
|
||||
class ShiftedDigits : public Digits {
|
||||
public:
|
||||
explicit ShiftedDigits(Digits& original, int shift = -1,
|
||||
bool allow_inplace = false)
|
||||
: Digits(original.digits_, original.len_) {
|
||||
int leading_zeros = CountLeadingZeros(original.msd());
|
||||
if (shift < 0) {
|
||||
shift = leading_zeros;
|
||||
} else if (shift > leading_zeros) {
|
||||
allow_inplace = false;
|
||||
len_++;
|
||||
}
|
||||
shift_ = shift;
|
||||
if (shift == 0) {
|
||||
inplace_ = true;
|
||||
return;
|
||||
}
|
||||
inplace_ = allow_inplace;
|
||||
if (!inplace_) {
|
||||
digit_t* digits = new digit_t[len_];
|
||||
storage_.reset(digits);
|
||||
digits_ = digits;
|
||||
}
|
||||
RWDigits rw_view(digits_, len_);
|
||||
LeftShift(rw_view, original, shift_);
|
||||
}
|
||||
~ShiftedDigits() = default;
|
||||
|
||||
void Reset() {
|
||||
if (inplace_) {
|
||||
RWDigits rw_view(digits_, len_);
|
||||
RightShift(rw_view, rw_view, shift_);
|
||||
}
|
||||
}
|
||||
|
||||
int shift() { return shift_; }
|
||||
|
||||
private:
|
||||
int shift_;
|
||||
bool inplace_;
|
||||
std::unique_ptr<digit_t[]> storage_;
|
||||
};
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_BIGINT_DIV_HELPERS_H_
|
191
src/bigint/div-schoolbook.cc
Normal file
191
src/bigint/div-schoolbook.cc
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// "Schoolbook" division. This is loosely based on Go's implementation
|
||||
// found at https://golang.org/src/math/big/nat.go, licensed as follows:
|
||||
//
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file [1].
|
||||
//
|
||||
// [1] https://golang.org/LICENSE
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "src/bigint/bigint-internal.h"
|
||||
#include "src/bigint/digit-arithmetic.h"
|
||||
#include "src/bigint/div-helpers.h"
|
||||
#include "src/bigint/util.h"
|
||||
#include "src/bigint/vector-arithmetic.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
|
||||
// Computes Q(uotient) and remainder for A/b, such that
|
||||
// Q = (A - remainder) / b, with 0 <= remainder < b.
|
||||
// If Q.len == 0, only the remainder will be returned.
|
||||
// Q may be the same as A for an in-place division.
|
||||
void ProcessorImpl::DivideSingle(RWDigits Q, digit_t* remainder, Digits A,
|
||||
digit_t b) {
|
||||
DCHECK(b != 0); // NOLINT(readability/check)
|
||||
DCHECK(A.len() > 0); // NOLINT(readability/check)
|
||||
*remainder = 0;
|
||||
int length = A.len();
|
||||
if (Q.len() != 0) {
|
||||
if (A[length - 1] >= b) {
|
||||
DCHECK(Q.len() >= A.len());
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
Q[i] = digit_div(*remainder, A[i], b, remainder);
|
||||
}
|
||||
for (int i = length; i < Q.len(); i++) Q[i] = 0;
|
||||
} else {
|
||||
DCHECK(Q.len() >= A.len() - 1);
|
||||
*remainder = A[length - 1];
|
||||
for (int i = length - 2; i >= 0; i--) {
|
||||
Q[i] = digit_div(*remainder, A[i], b, remainder);
|
||||
}
|
||||
for (int i = length - 1; i < Q.len(); i++) Q[i] = 0;
|
||||
}
|
||||
} else {
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
digit_div(*remainder, A[i], b, remainder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Z += X. Returns the "carry" (0 or 1) after adding all of X's digits.
|
||||
inline digit_t InplaceAdd(RWDigits Z, Digits X) {
|
||||
return AddAndReturnCarry(Z, Z, X);
|
||||
}
|
||||
|
||||
// Z -= X. Returns the "borrow" (0 or 1) after subtracting all of X's digits.
|
||||
inline digit_t InplaceSub(RWDigits Z, Digits X) {
|
||||
return SubtractAndReturnBorrow(Z, Z, X);
|
||||
}
|
||||
|
||||
// Returns whether (factor1 * factor2) > (high << kDigitBits) + low.
|
||||
bool ProductGreaterThan(digit_t factor1, digit_t factor2, digit_t high,
|
||||
digit_t low) {
|
||||
digit_t result_high;
|
||||
digit_t result_low = digit_mul(factor1, factor2, &result_high);
|
||||
return result_high > high || (result_high == high && result_low > low);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
bool QLengthOK(Digits Q, Digits A, Digits B) {
|
||||
// If A's top B.len digits are greater than or equal to B, then the division
|
||||
// result will be greater than A.len - B.len, otherwise it will be that
|
||||
// difference. Intuitively: 100/10 has 2 digits, 100/11 has 1.
|
||||
if (GreaterThanOrEqual(Digits(A, A.len() - B.len(), B.len()), B)) {
|
||||
return Q.len() >= A.len() - B.len() + 1;
|
||||
}
|
||||
return Q.len() >= A.len() - B.len();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Computes Q(uotient) and R(emainder) for A/B, such that
|
||||
// Q = (A - R) / B, with 0 <= R < B.
|
||||
// Both Q and R are optional: callers that are only interested in one of them
|
||||
// can pass the other with len == 0.
|
||||
// If Q is present, its length must be at least A.len - B.len + 1.
|
||||
// If R is present, its length must be at least B.len.
|
||||
// See Knuth, Volume 2, section 4.3.1, Algorithm D.
|
||||
void ProcessorImpl::DivideSchoolbook(RWDigits Q, RWDigits R, Digits A,
|
||||
Digits B) {
|
||||
// NOLINTNEXTLINE(readability/check)
|
||||
DCHECK(B.len() >= 2); // Use DivideSingle otherwise.
|
||||
DCHECK(A.len() >= B.len()); // No-op otherwise.
|
||||
DCHECK(Q.len() == 0 || QLengthOK(Q, A, B));
|
||||
DCHECK(R.len() == 0 || R.len() >= B.len());
|
||||
// The unusual variable names inside this function are consistent with
|
||||
// Knuth's book, as well as with Go's implementation of this algorithm.
|
||||
// Maintaining this consistency is probably more useful than trying to
|
||||
// come up with more descriptive names for them.
|
||||
const int n = B.len();
|
||||
const int m = A.len() - n;
|
||||
|
||||
// In each iteration, {qhatv} holds {divisor} * {current quotient digit}.
|
||||
// "v" is the book's name for {divisor}, "qhat" the current quotient digit.
|
||||
ScratchDigits qhatv(n + 1);
|
||||
|
||||
// D1.
|
||||
// Left-shift inputs so that the divisor's MSB is set. This is necessary
|
||||
// to prevent the digit-wise divisions (see digit_div call below) from
|
||||
// overflowing (they take a two digits wide input, and return a one digit
|
||||
// result).
|
||||
ShiftedDigits b_normalized(B);
|
||||
B = b_normalized;
|
||||
// U holds the (continuously updated) remaining part of the dividend, which
|
||||
// eventually becomes the remainder.
|
||||
ScratchDigits U(A.len() + 1);
|
||||
LeftShift(U, A, b_normalized.shift());
|
||||
|
||||
// D2.
|
||||
// Iterate over the dividend's digits (like the "grad school" algorithm).
|
||||
// {vn1} is the divisor's most significant digit.
|
||||
digit_t vn1 = B[n - 1];
|
||||
for (int j = m; j >= 0; j--) {
|
||||
// D3.
|
||||
// Estimate the current iteration's quotient digit (see Knuth for details).
|
||||
// {qhat} is the current quotient digit.
|
||||
digit_t qhat = std::numeric_limits<digit_t>::max();
|
||||
// {ujn} is the dividend's most significant remaining digit.
|
||||
digit_t ujn = U[j + n];
|
||||
if (ujn != vn1) {
|
||||
// {rhat} is the current iteration's remainder.
|
||||
digit_t rhat = 0;
|
||||
// Estimate the current quotient digit by dividing the most significant
|
||||
// digits of dividend and divisor. The result will not be too small,
|
||||
// but could be a bit too large.
|
||||
qhat = digit_div(ujn, U[j + n - 1], vn1, &rhat);
|
||||
|
||||
// Decrement the quotient estimate as needed by looking at the next
|
||||
// digit, i.e. by testing whether
|
||||
// qhat * v_{n-2} > (rhat << kDigitBits) + u_{j+n-2}.
|
||||
digit_t vn2 = B[n - 2];
|
||||
digit_t ujn2 = U[j + n - 2];
|
||||
while (ProductGreaterThan(qhat, vn2, rhat, ujn2)) {
|
||||
qhat--;
|
||||
digit_t prev_rhat = rhat;
|
||||
rhat += vn1;
|
||||
// v[n-1] >= 0, so this tests for overflow.
|
||||
if (rhat < prev_rhat) break;
|
||||
}
|
||||
}
|
||||
|
||||
// D4.
|
||||
// Multiply the divisor with the current quotient digit, and subtract
|
||||
// it from the dividend. If there was "borrow", then the quotient digit
|
||||
// was one too high, so we must correct it and undo one subtraction of
|
||||
// the (shifted) divisor.
|
||||
if (qhat == 0) {
|
||||
qhatv.Clear();
|
||||
} else {
|
||||
MultiplySingle(qhatv, B, qhat);
|
||||
if (should_terminate()) return;
|
||||
}
|
||||
digit_t c = InplaceSub(U + j, qhatv);
|
||||
if (c != 0) {
|
||||
c = InplaceAdd(U + j, B);
|
||||
U[j + n] = U[j + n] + c;
|
||||
qhat--;
|
||||
}
|
||||
|
||||
if (Q.len() != 0) {
|
||||
if (j >= Q.len()) {
|
||||
DCHECK(qhat == 0); // NOLINT(readability/check)
|
||||
} else {
|
||||
Q[j] = qhat;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (R.len() != 0) {
|
||||
RightShift(R, U, b_normalized.shift());
|
||||
}
|
||||
// If Q has extra storage, clear it.
|
||||
for (int i = m + 1; i < Q.len(); i++) Q[i] = 0;
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/bigint/vector-arithmetic.h"
|
||||
|
||||
#include "src/bigint/bigint-internal.h"
|
||||
#include "src/bigint/digit-arithmetic.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -34,5 +35,23 @@ void SubAt(RWDigits Z, Digits X) {
|
||||
}
|
||||
}
|
||||
|
||||
digit_t AddAndReturnCarry(RWDigits Z, Digits X, Digits Y) {
|
||||
DCHECK(Z.len() >= Y.len() && X.len() >= Y.len());
|
||||
digit_t carry = 0;
|
||||
for (int i = 0; i < Y.len(); i++) {
|
||||
Z[i] = digit_add3(X[i], Y[i], carry, &carry);
|
||||
}
|
||||
return carry;
|
||||
}
|
||||
|
||||
digit_t SubtractAndReturnBorrow(RWDigits Z, Digits X, Digits Y) {
|
||||
DCHECK(Z.len() >= Y.len() && X.len() >= Y.len());
|
||||
digit_t borrow = 0;
|
||||
for (int i = 0; i < Y.len(); i++) {
|
||||
Z[i] = digit_sub2(X[i], Y[i], borrow, &borrow);
|
||||
}
|
||||
return borrow;
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
@ -18,6 +18,11 @@ void AddAt(RWDigits Z, Digits X);
|
||||
// Z -= X.
|
||||
void SubAt(RWDigits Z, Digits X);
|
||||
|
||||
// These add exactly Y's digits to the matching digits in X, storing the
|
||||
// result in (part of) Z, and return the carry/borrow.
|
||||
digit_t AddAndReturnCarry(RWDigits Z, Digits X, Digits Y);
|
||||
digit_t SubtractAndReturnBorrow(RWDigits Z, Digits X, Digits Y);
|
||||
|
||||
inline bool IsDigitNormalized(Digits X) { return X.len() == 0 || X.msd() != 0; }
|
||||
|
||||
inline bool GreaterThanOrEqual(Digits A, Digits B) {
|
||||
|
@ -143,23 +143,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
|
||||
static void AbsoluteDivSmall(Isolate* isolate, Handle<BigIntBase> x,
|
||||
digit_t divisor, Handle<MutableBigInt>* quotient,
|
||||
digit_t* remainder);
|
||||
static bool AbsoluteDivLarge(Isolate* isolate, Handle<BigIntBase> dividend,
|
||||
Handle<BigIntBase> divisor,
|
||||
Handle<MutableBigInt>* quotient,
|
||||
Handle<MutableBigInt>* remainder);
|
||||
static bool ProductGreaterThan(digit_t factor1, digit_t factor2, digit_t high,
|
||||
digit_t low);
|
||||
digit_t InplaceAdd(Handle<BigIntBase> summand, int start_index);
|
||||
digit_t InplaceSub(Handle<BigIntBase> subtrahend, int start_index);
|
||||
void InplaceRightShift(int shift);
|
||||
enum SpecialLeftShiftMode {
|
||||
kSameSizeResult,
|
||||
kAlwaysAddOneDigit,
|
||||
};
|
||||
static MaybeHandle<MutableBigInt> SpecialLeftShift(Isolate* isolate,
|
||||
Handle<BigIntBase> x,
|
||||
int shift,
|
||||
SpecialLeftShiftMode mode);
|
||||
|
||||
// Specialized helpers for shift operations.
|
||||
static MaybeHandle<BigInt> LeftShiftByAbsolute(Isolate* isolate,
|
||||
@ -566,21 +549,24 @@ MaybeHandle<BigInt> BigInt::Divide(Isolate* isolate, Handle<BigInt> x,
|
||||
if (bigint::Compare(GetDigits(x), GetDigits(y)) < 0) {
|
||||
return Zero(isolate);
|
||||
}
|
||||
Handle<MutableBigInt> quotient;
|
||||
bool result_sign = x->sign() != y->sign();
|
||||
if (y->length() == 1) {
|
||||
digit_t divisor = y->digit(0);
|
||||
if (divisor == 1) {
|
||||
return result_sign == x->sign() ? x : UnaryMinus(isolate, x);
|
||||
}
|
||||
digit_t remainder;
|
||||
MutableBigInt::AbsoluteDivSmall(isolate, x, divisor, "ient, &remainder);
|
||||
} else {
|
||||
if (!MutableBigInt::AbsoluteDivLarge(isolate, x, y, "ient, nullptr)) {
|
||||
return MaybeHandle<BigInt>();
|
||||
}
|
||||
if (y->length() == 1 && y->digit(0) == 1) {
|
||||
return result_sign == x->sign() ? x : UnaryMinus(isolate, x);
|
||||
}
|
||||
quotient->set_sign(x->sign() != y->sign());
|
||||
Handle<MutableBigInt> quotient;
|
||||
int result_length = bigint::DivideResultLength(GetDigits(x), GetDigits(y));
|
||||
if (!MutableBigInt::New(isolate, result_length).ToHandle("ient)) {
|
||||
return {};
|
||||
}
|
||||
DisallowGarbageCollection no_gc;
|
||||
bigint::Status status = isolate->bigint_processor()->Divide(
|
||||
GetRWDigits(quotient), GetDigits(x), GetDigits(y));
|
||||
if (status == bigint::Status::kInterrupted) {
|
||||
AllowGarbageCollection terminating_anyway;
|
||||
isolate->TerminateExecution();
|
||||
return {};
|
||||
}
|
||||
quotient->set_sign(result_sign);
|
||||
return MutableBigInt::MakeImmutable(quotient);
|
||||
}
|
||||
|
||||
@ -594,22 +580,19 @@ MaybeHandle<BigInt> BigInt::Remainder(Isolate* isolate, Handle<BigInt> x,
|
||||
// 2. Return the BigInt representing x modulo y.
|
||||
// See https://github.com/tc39/proposal-bigint/issues/84 though.
|
||||
if (bigint::Compare(GetDigits(x), GetDigits(y)) < 0) return x;
|
||||
if (y->length() == 1 && y->digit(0) == 1) return Zero(isolate);
|
||||
Handle<MutableBigInt> remainder;
|
||||
if (y->length() == 1) {
|
||||
digit_t divisor = y->digit(0);
|
||||
if (divisor == 1) return Zero(isolate);
|
||||
digit_t remainder_digit;
|
||||
MutableBigInt::AbsoluteDivSmall(isolate, x, divisor, nullptr,
|
||||
&remainder_digit);
|
||||
if (remainder_digit == 0) {
|
||||
return Zero(isolate);
|
||||
}
|
||||
remainder = MutableBigInt::New(isolate, 1).ToHandleChecked();
|
||||
remainder->set_digit(0, remainder_digit);
|
||||
} else {
|
||||
if (!MutableBigInt::AbsoluteDivLarge(isolate, x, y, nullptr, &remainder)) {
|
||||
return MaybeHandle<BigInt>();
|
||||
}
|
||||
int result_length = bigint::ModuloResultLength(GetDigits(y));
|
||||
if (!MutableBigInt::New(isolate, result_length).ToHandle(&remainder)) {
|
||||
return {};
|
||||
}
|
||||
DisallowGarbageCollection no_gc;
|
||||
bigint::Status status = isolate->bigint_processor()->Modulo(
|
||||
GetRWDigits(remainder), GetDigits(x), GetDigits(y));
|
||||
if (status == bigint::Status::kInterrupted) {
|
||||
AllowGarbageCollection terminating_anyway;
|
||||
isolate->TerminateExecution();
|
||||
return {};
|
||||
}
|
||||
remainder->set_sign(x->sign());
|
||||
return MutableBigInt::MakeImmutable(remainder);
|
||||
@ -1517,220 +1500,6 @@ void MutableBigInt::AbsoluteDivSmall(Isolate* isolate, Handle<BigIntBase> x,
|
||||
}
|
||||
}
|
||||
|
||||
// Divides {dividend} by {divisor}, returning the result in {quotient} and
|
||||
// {remainder}. Mathematically, the contract is:
|
||||
// quotient = (dividend - remainder) / divisor, with 0 <= remainder < divisor.
|
||||
// Both {quotient} and {remainder} are optional, for callers that are only
|
||||
// interested in one of them.
|
||||
// See Knuth, Volume 2, section 4.3.1, Algorithm D.
|
||||
bool MutableBigInt::AbsoluteDivLarge(Isolate* isolate,
|
||||
Handle<BigIntBase> dividend,
|
||||
Handle<BigIntBase> divisor,
|
||||
Handle<MutableBigInt>* quotient,
|
||||
Handle<MutableBigInt>* remainder) {
|
||||
DCHECK_GE(divisor->length(), 2);
|
||||
DCHECK(dividend->length() >= divisor->length());
|
||||
// The unusual variable names inside this function are consistent with
|
||||
// Knuth's book, as well as with Go's implementation of this algorithm.
|
||||
// Maintaining this consistency is probably more useful than trying to
|
||||
// come up with more descriptive names for them.
|
||||
int n = divisor->length();
|
||||
int m = dividend->length() - n;
|
||||
|
||||
// The quotient to be computed.
|
||||
Handle<MutableBigInt> q;
|
||||
if (quotient != nullptr) q = New(isolate, m + 1).ToHandleChecked();
|
||||
// In each iteration, {qhatv} holds {divisor} * {current quotient digit}.
|
||||
// "v" is the book's name for {divisor}, "qhat" the current quotient digit.
|
||||
Handle<MutableBigInt> qhatv;
|
||||
if (!New(isolate, n + 1).ToHandle(&qhatv)) return false;
|
||||
|
||||
// D1.
|
||||
// Left-shift inputs so that the divisor's MSB is set. This is necessary
|
||||
// to prevent the digit-wise divisions (see digit_div call below) from
|
||||
// overflowing (they take a two digits wide input, and return a one digit
|
||||
// result).
|
||||
int shift = base::bits::CountLeadingZeros(divisor->digit(n - 1));
|
||||
if (shift > 0) {
|
||||
divisor = SpecialLeftShift(isolate, divisor, shift, kSameSizeResult)
|
||||
.ToHandleChecked();
|
||||
}
|
||||
// Holds the (continuously updated) remaining part of the dividend, which
|
||||
// eventually becomes the remainder.
|
||||
Handle<MutableBigInt> u;
|
||||
if (!SpecialLeftShift(isolate, dividend, shift, kAlwaysAddOneDigit)
|
||||
.ToHandle(&u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// D2.
|
||||
// Iterate over the dividend's digit (like the "grad school" algorithm).
|
||||
// {vn1} is the divisor's most significant digit.
|
||||
digit_t vn1 = divisor->digit(n - 1);
|
||||
uintptr_t work_estimate = 0;
|
||||
for (int j = m; j >= 0; j--) {
|
||||
// D3.
|
||||
// Estimate the current iteration's quotient digit (see Knuth for details).
|
||||
// {qhat} is the current quotient digit.
|
||||
digit_t qhat = std::numeric_limits<digit_t>::max();
|
||||
// {ujn} is the dividend's most significant remaining digit.
|
||||
digit_t ujn = u->digit(j + n);
|
||||
if (ujn != vn1) {
|
||||
// {rhat} is the current iteration's remainder.
|
||||
digit_t rhat = 0;
|
||||
// Estimate the current quotient digit by dividing the most significant
|
||||
// digits of dividend and divisor. The result will not be too small,
|
||||
// but could be a bit too large.
|
||||
qhat = digit_div(ujn, u->digit(j + n - 1), vn1, &rhat);
|
||||
|
||||
// Decrement the quotient estimate as needed by looking at the next
|
||||
// digit, i.e. by testing whether
|
||||
// qhat * v_{n-2} > (rhat << kDigitBits) + u_{j+n-2}.
|
||||
digit_t vn2 = divisor->digit(n - 2);
|
||||
digit_t ujn2 = u->digit(j + n - 2);
|
||||
while (ProductGreaterThan(qhat, vn2, rhat, ujn2)) {
|
||||
qhat--;
|
||||
digit_t prev_rhat = rhat;
|
||||
rhat += vn1;
|
||||
// v[n-1] >= 0, so this tests for overflow.
|
||||
if (rhat < prev_rhat) break;
|
||||
}
|
||||
}
|
||||
|
||||
// D4.
|
||||
// Multiply the divisor with the current quotient digit, and subtract
|
||||
// it from the dividend. If there was "borrow", then the quotient digit
|
||||
// was one too high, so we must correct it and undo one subtraction of
|
||||
// the (shifted) divisor.
|
||||
InternalMultiplyAdd(*divisor, qhat, 0, n, *qhatv);
|
||||
digit_t c = u->InplaceSub(qhatv, j);
|
||||
if (c != 0) {
|
||||
c = u->InplaceAdd(divisor, j);
|
||||
u->set_digit(j + n, u->digit(j + n) + c);
|
||||
qhat--;
|
||||
}
|
||||
|
||||
if (quotient != nullptr) q->set_digit(j, qhat);
|
||||
|
||||
// Division can take a long time. Check for interrupt requests every
|
||||
// now and then (roughly every 10-20 of milliseconds -- rarely enough
|
||||
// not to create noticeable overhead, frequently enough not to appear
|
||||
// frozen).
|
||||
work_estimate += n;
|
||||
if (work_estimate > 5000000) {
|
||||
work_estimate = 0;
|
||||
StackLimitCheck interrupt_check(isolate);
|
||||
if (interrupt_check.InterruptRequested() &&
|
||||
isolate->stack_guard()->HandleInterrupts().IsException(isolate)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (quotient != nullptr) {
|
||||
*quotient = q; // Caller will right-trim.
|
||||
}
|
||||
if (remainder != nullptr) {
|
||||
u->InplaceRightShift(shift);
|
||||
*remainder = u;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns whether (factor1 * factor2) > (high << kDigitBits) + low.
|
||||
bool MutableBigInt::ProductGreaterThan(digit_t factor1, digit_t factor2,
|
||||
digit_t high, digit_t low) {
|
||||
digit_t result_high;
|
||||
digit_t result_low = digit_mul(factor1, factor2, &result_high);
|
||||
return result_high > high || (result_high == high && result_low > low);
|
||||
}
|
||||
|
||||
// Adds {summand} onto {this}, starting with {summand}'s 0th digit
|
||||
// at {this}'s {start_index}'th digit. Returns the "carry" (0 or 1).
|
||||
BigInt::digit_t MutableBigInt::InplaceAdd(Handle<BigIntBase> summand,
|
||||
int start_index) {
|
||||
digit_t carry = 0;
|
||||
int n = summand->length();
|
||||
DCHECK(length() >= start_index + n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
digit_t new_carry = 0;
|
||||
digit_t sum =
|
||||
digit_add(digit(start_index + i), summand->digit(i), &new_carry);
|
||||
sum = digit_add(sum, carry, &new_carry);
|
||||
set_digit(start_index + i, sum);
|
||||
carry = new_carry;
|
||||
}
|
||||
return carry;
|
||||
}
|
||||
|
||||
// Subtracts {subtrahend} from {this}, starting with {subtrahend}'s 0th digit
|
||||
// at {this}'s {start_index}-th digit. Returns the "borrow" (0 or 1).
|
||||
BigInt::digit_t MutableBigInt::InplaceSub(Handle<BigIntBase> subtrahend,
|
||||
int start_index) {
|
||||
digit_t borrow = 0;
|
||||
int n = subtrahend->length();
|
||||
DCHECK(length() >= start_index + n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
digit_t new_borrow = 0;
|
||||
digit_t difference =
|
||||
digit_sub(digit(start_index + i), subtrahend->digit(i), &new_borrow);
|
||||
difference = digit_sub(difference, borrow, &new_borrow);
|
||||
set_digit(start_index + i, difference);
|
||||
borrow = new_borrow;
|
||||
}
|
||||
return borrow;
|
||||
}
|
||||
|
||||
void MutableBigInt::InplaceRightShift(int shift) {
|
||||
DCHECK_GE(shift, 0);
|
||||
DCHECK_LT(shift, kDigitBits);
|
||||
DCHECK_GT(length(), 0);
|
||||
DCHECK_EQ(digit(0) & ((static_cast<digit_t>(1) << shift) - 1), 0);
|
||||
if (shift == 0) return;
|
||||
digit_t carry = digit(0) >> shift;
|
||||
int last = length() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
digit_t d = digit(i + 1);
|
||||
set_digit(i, (d << (kDigitBits - shift)) | carry);
|
||||
carry = d >> shift;
|
||||
}
|
||||
set_digit(last, carry);
|
||||
}
|
||||
|
||||
// Always copies the input, even when {shift} == 0.
|
||||
// {shift} must be less than kDigitBits, {x} must be non-zero.
|
||||
MaybeHandle<MutableBigInt> MutableBigInt::SpecialLeftShift(
|
||||
Isolate* isolate, Handle<BigIntBase> x, int shift,
|
||||
SpecialLeftShiftMode mode) {
|
||||
DCHECK_GE(shift, 0);
|
||||
DCHECK_LT(shift, kDigitBits);
|
||||
DCHECK_GT(x->length(), 0);
|
||||
int n = x->length();
|
||||
int result_length = mode == kAlwaysAddOneDigit ? n + 1 : n;
|
||||
Handle<MutableBigInt> result;
|
||||
if (!New(isolate, result_length).ToHandle(&result)) {
|
||||
return MaybeHandle<MutableBigInt>();
|
||||
}
|
||||
if (shift == 0) {
|
||||
for (int i = 0; i < n; i++) result->set_digit(i, x->digit(i));
|
||||
if (mode == kAlwaysAddOneDigit) result->set_digit(n, 0);
|
||||
return result;
|
||||
}
|
||||
DCHECK_GT(shift, 0);
|
||||
digit_t carry = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
digit_t d = x->digit(i);
|
||||
result->set_digit(i, (d << shift) | carry);
|
||||
carry = d >> (kDigitBits - shift);
|
||||
}
|
||||
if (mode == kAlwaysAddOneDigit) {
|
||||
result->set_digit(n, carry);
|
||||
} else {
|
||||
DCHECK_EQ(mode, kSameSizeResult);
|
||||
DCHECK_EQ(carry, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
|
||||
Handle<BigIntBase> x,
|
||||
Handle<BigIntBase> y) {
|
||||
|
8
test/mjsunit/harmony/bigint/mod-special-cases.js
Normal file
8
test/mjsunit/harmony/bigint/mod-special-cases.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
let quotient = 0x1234567890abcdef12345678n; // Bigger than one digit_t.
|
||||
let dividend = quotient * 10n;
|
||||
let result = dividend % quotient;
|
||||
assertEquals(0n, result);
|
Loading…
Reference in New Issue
Block a user