From b8ae525e8e9a65191581e5372fe773d2f23dd1ac Mon Sep 17 00:00:00 2001 From: Nico Hartmann Date: Mon, 6 Dec 2021 14:52:22 +0100 Subject: [PATCH] [BigInt] Port BigInt left and right shift from src/objects/bigint.cc to src/bigint/bitwise.cc. Bug: v8:11515 Change-Id: I20f8aebab138651247cedcd85460e40fbc255d98 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3310802 Reviewed-by: Jakob Kummerow Commit-Queue: Nico Hartmann Cr-Commit-Position: refs/heads/main@{#78290} --- src/bigint/bigint.h | 19 +++++++ src/bigint/bitwise.cc | 85 +++++++++++++++++++++++++++++++ src/bigint/digit-arithmetic.h | 2 + src/objects/bigint.cc | 95 ++++------------------------------- 4 files changed, 117 insertions(+), 84 deletions(-) diff --git a/src/bigint/bigint.h b/src/bigint/bigint.h index 28df2936ac..300229c97d 100644 --- a/src/bigint/bigint.h +++ b/src/bigint/bigint.h @@ -253,6 +253,14 @@ void BitwiseOr_PosNeg(RWDigits Z, Digits X, Digits Y); void BitwiseXor_PosPos(RWDigits Z, Digits X, Digits Y); void BitwiseXor_NegNeg(RWDigits Z, Digits X, Digits Y); void BitwiseXor_PosNeg(RWDigits Z, Digits X, Digits Y); +void LeftShift(RWDigits Z, Digits X, digit_t shift); +// RightShiftState is provided by RightShift_ResultLength and used by the actual +// RightShift to avoid some recomputation. +struct RightShiftState { + bool must_round_down = false; +}; +void RightShift(RWDigits Z, Digits X, digit_t shift, + const RightShiftState& state); // Z := (least significant n bits of X, interpreted as a signed n-bit integer). // Returns true if the result is negative; Z will hold the absolute value. @@ -352,6 +360,17 @@ inline int BitwiseXor_PosNeg_ResultLength(int x_length, int y_length) { // Result length growth example: 3 ^ -1 == -4 (2-bit inputs, 3-bit result). return std::max(x_length, y_length) + 1; } +inline int LeftShift_ResultLength(int x_length, + digit_t x_most_significant_digit, + digit_t shift) { + int digit_shift = static_cast(shift / kDigitBits); + int bits_shift = static_cast(shift % kDigitBits); + bool grow = bits_shift != 0 && + (x_most_significant_digit >> (kDigitBits - bits_shift)) != 0; + return x_length + digit_shift + grow; +} +int RightShift_ResultLength(Digits X, bool x_sign, digit_t shift, + RightShiftState* state); // Returns -1 if this "asIntN" operation would be a no-op. int AsIntNResultLength(Digits X, bool x_negative, int n); diff --git a/src/bigint/bitwise.cc b/src/bigint/bitwise.cc index d18d27c1ac..c4cec22b53 100644 --- a/src/bigint/bitwise.cc +++ b/src/bigint/bitwise.cc @@ -133,6 +133,91 @@ void BitwiseXor_PosNeg(RWDigits Z, Digits X, Digits Y) { Add(Z, 1); } +void LeftShift(RWDigits Z, Digits X, digit_t shift) { + int digit_shift = static_cast(shift / kDigitBits); + int bits_shift = static_cast(shift % kDigitBits); + + int i = 0; + for (; i < digit_shift; ++i) Z[i] = 0; + if (bits_shift == 0) { + for (; i < X.len() + digit_shift; ++i) Z[i] = X[i - digit_shift]; + for (; i < Z.len(); ++i) Z[i] = 0; + } else { + digit_t carry = 0; + for (; i < X.len() + digit_shift; ++i) { + digit_t d = X[i - digit_shift]; + Z[i] = (d << bits_shift) | carry; + carry = d >> (kDigitBits - bits_shift); + } + if (carry != 0) Z[i++] = carry; + for (; i < Z.len(); ++i) Z[i] = 0; + } +} + +int RightShift_ResultLength(Digits X, bool x_sign, digit_t shift, + RightShiftState* state) { + int digit_shift = static_cast(shift / kDigitBits); + int bits_shift = static_cast(shift % kDigitBits); + int result_length = X.len() - digit_shift; + if (result_length <= 0) return 0; + + // For negative numbers, round down if any bit was shifted out (so that e.g. + // -5n >> 1n == -3n and not -2n). Check now whether this will happen and + // whether it can cause overflow into a new digit. + bool must_round_down = false; + if (x_sign) { + const digit_t mask = (static_cast(1) << bits_shift) - 1; + if ((X[digit_shift] & mask) != 0) { + must_round_down = true; + } else { + for (int i = 0; i < digit_shift; i++) { + if (X[i] != 0) { + must_round_down = true; + break; + } + } + } + } + // If bits_shift is non-zero, it frees up bits, preventing overflow. + if (must_round_down && bits_shift == 0) { + // Overflow cannot happen if the most significant digit has unset bits. + const bool rounding_can_overflow = digit_ismax(X.msd()); + if (rounding_can_overflow) ++result_length; + } + + if (state) { + DCHECK(!must_round_down || x_sign); + state->must_round_down = must_round_down; + } + return result_length; +} + +void RightShift(RWDigits Z, Digits X, digit_t shift, + const RightShiftState& state) { + int digit_shift = static_cast(shift / kDigitBits); + int bits_shift = static_cast(shift % kDigitBits); + + int i = 0; + if (bits_shift == 0) { + for (; i < X.len() - digit_shift; ++i) Z[i] = X[i + digit_shift]; + } else { + digit_t carry = X[digit_shift] >> bits_shift; + for (; i < X.len() - digit_shift - 1; ++i) { + digit_t d = X[i + digit_shift + 1]; + Z[i] = (d << (kDigitBits - bits_shift)) | carry; + carry = d >> bits_shift; + } + Z[i++] = carry; + } + for (; i < Z.len(); ++i) Z[i] = 0; + + if (state.must_round_down) { + // Rounding down (a negative value) means adding one to + // its absolute value. This cannot overflow. + Add(Z, 1); + } +} + namespace { // Z := (least significant n bits of X). diff --git a/src/bigint/digit-arithmetic.h b/src/bigint/digit-arithmetic.h index 45ffbea447..d9113efc91 100644 --- a/src/bigint/digit-arithmetic.h +++ b/src/bigint/digit-arithmetic.h @@ -17,6 +17,8 @@ static constexpr int kHalfDigitBits = kDigitBits / 2; static constexpr digit_t kHalfDigitBase = digit_t{1} << kHalfDigitBits; static constexpr digit_t kHalfDigitMask = kHalfDigitBase - 1; +constexpr bool digit_ismax(digit_t x) { return static_cast(~x) == 0; } + // {carry} will be set to 0 or 1. inline digit_t digit_add2(digit_t a, digit_t b, digit_t* carry) { #if HAVE_TWODIGIT_T diff --git a/src/objects/bigint.cc b/src/objects/bigint.cc index 5f323aa4ec..aa9ff9d30b 100644 --- a/src/objects/bigint.cc +++ b/src/objects/bigint.cc @@ -1247,12 +1247,8 @@ MaybeHandle MutableBigInt::LeftShiftByAbsolute(Isolate* isolate, return ThrowBigIntTooBig(isolate); } digit_t shift = maybe_shift.FromJust(); - int digit_shift = static_cast(shift / kDigitBits); - int bits_shift = static_cast(shift % kDigitBits); - int length = x->length(); - bool grow = bits_shift != 0 && - (x->digit(length - 1) >> (kDigitBits - bits_shift)) != 0; - int result_length = length + digit_shift + grow; + const int result_length = bigint::LeftShift_ResultLength( + x->length(), x->digit(x->length() - 1), shift); if (result_length > kMaxLength) { return ThrowBigIntTooBig(isolate); } @@ -1260,26 +1256,7 @@ MaybeHandle MutableBigInt::LeftShiftByAbsolute(Isolate* isolate, if (!New(isolate, result_length).ToHandle(&result)) { return MaybeHandle(); } - if (bits_shift == 0) { - int i = 0; - for (; i < digit_shift; i++) result->set_digit(i, 0ul); - for (; i < result_length; i++) { - result->set_digit(i, x->digit(i - digit_shift)); - } - } else { - digit_t carry = 0; - for (int i = 0; i < digit_shift; i++) result->set_digit(i, 0ul); - for (int i = 0; i < length; i++) { - digit_t d = x->digit(i); - result->set_digit(i + digit_shift, (d << bits_shift) | carry); - carry = d >> (kDigitBits - bits_shift); - } - if (grow) { - result->set_digit(length + digit_shift, carry); - } else { - DCHECK_EQ(carry, 0); - } - } + bigint::LeftShift(GetRWDigits(result), GetDigits(x), shift); result->set_sign(x->sign()); return MakeImmutable(result); } @@ -1287,72 +1264,22 @@ MaybeHandle MutableBigInt::LeftShiftByAbsolute(Isolate* isolate, Handle MutableBigInt::RightShiftByAbsolute(Isolate* isolate, Handle x, Handle y) { - int length = x->length(); - bool sign = x->sign(); + const bool sign = x->sign(); Maybe maybe_shift = ToShiftAmount(y); if (maybe_shift.IsNothing()) { return RightShiftByMaximum(isolate, sign); } - digit_t shift = maybe_shift.FromJust(); - int digit_shift = static_cast(shift / kDigitBits); - int bits_shift = static_cast(shift % kDigitBits); - int result_length = length - digit_shift; + const digit_t shift = maybe_shift.FromJust(); + bigint::RightShiftState state; + const int result_length = + bigint::RightShift_ResultLength(GetDigits(x), sign, shift, &state); + DCHECK_LE(result_length, x->length()); if (result_length <= 0) { return RightShiftByMaximum(isolate, sign); } - // For negative numbers, round down if any bit was shifted out (so that e.g. - // -5n >> 1n == -3n and not -2n). Check now whether this will happen and - // whether it can cause overflow into a new digit. If we allocate the result - // large enough up front, it avoids having to do a second allocation later. - bool must_round_down = false; - if (sign) { - const digit_t mask = (static_cast(1) << bits_shift) - 1; - if ((x->digit(digit_shift) & mask) != 0) { - must_round_down = true; - } else { - for (int i = 0; i < digit_shift; i++) { - if (x->digit(i) != 0) { - must_round_down = true; - break; - } - } - } - } - // If bits_shift is non-zero, it frees up bits, preventing overflow. - if (must_round_down && bits_shift == 0) { - // Overflow cannot happen if the most significant digit has unset bits. - digit_t msd = x->digit(length - 1); - bool rounding_can_overflow = digit_ismax(msd); - if (rounding_can_overflow) result_length++; - } - - DCHECK_LE(result_length, length); Handle result = New(isolate, result_length).ToHandleChecked(); - if (bits_shift == 0) { - // Zero out any overflow digit (see "rounding_can_overflow" above). - result->set_digit(result_length - 1, 0); - for (int i = digit_shift; i < length; i++) { - result->set_digit(i - digit_shift, x->digit(i)); - } - } else { - digit_t carry = x->digit(digit_shift) >> bits_shift; - int last = length - digit_shift - 1; - for (int i = 0; i < last; i++) { - digit_t d = x->digit(i + digit_shift + 1); - result->set_digit(i, (d << (kDigitBits - bits_shift)) | carry); - carry = d >> bits_shift; - } - result->set_digit(last, carry); - } - - if (sign) { - result->set_sign(true); - if (must_round_down) { - // Since the result is negative, rounding down means adding one to - // its absolute value. This cannot overflow. - result = AbsoluteAddOne(isolate, result, true, *result).ToHandleChecked(); - } - } + bigint::RightShift(GetRWDigits(result), GetDigits(x), shift, state); + if (sign) result->set_sign(true); return MakeImmutable(result); }