[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 <jkummerow@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78290}
This commit is contained in:
Nico Hartmann 2021-12-06 14:52:22 +01:00 committed by V8 LUCI CQ
parent b3e1eb0c0d
commit b8ae525e8e
4 changed files with 117 additions and 84 deletions

View File

@ -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<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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);

View File

@ -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<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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<digit_t>(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<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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).

View File

@ -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<digit_t>(~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

View File

@ -1247,12 +1247,8 @@ MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
return ThrowBigIntTooBig<BigInt>(isolate);
}
digit_t shift = maybe_shift.FromJust();
int digit_shift = static_cast<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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<BigInt>(isolate);
}
@ -1260,26 +1256,7 @@ MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
if (!New(isolate, result_length).ToHandle(&result)) {
return MaybeHandle<BigInt>();
}
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<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
Handle<BigInt> MutableBigInt::RightShiftByAbsolute(Isolate* isolate,
Handle<BigIntBase> x,
Handle<BigIntBase> y) {
int length = x->length();
bool sign = x->sign();
const bool sign = x->sign();
Maybe<digit_t> maybe_shift = ToShiftAmount(y);
if (maybe_shift.IsNothing()) {
return RightShiftByMaximum(isolate, sign);
}
digit_t shift = maybe_shift.FromJust();
int digit_shift = static_cast<int>(shift / kDigitBits);
int bits_shift = static_cast<int>(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<digit_t>(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<MutableBigInt> 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);
}