[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:
parent
b3e1eb0c0d
commit
b8ae525e8e
@ -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);
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user