[bigint] Move bitwise ops to src/bigint/

No asymptotic improvements, and none are planned either.
Minor speedups (25-50%) through reduced overhead: accessing Digits
is faster than working with Handle<BigInt>, and this implementation
avoids allocating intermediate results.

Bug: v8:11515
Change-Id: I2aab2b1c5c9cbb910800161b8514c497daf2b587
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3149453
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76793}
This commit is contained in:
Jakob Kummerow 2021-09-09 01:19:07 +02:00 committed by V8 LUCI CQ
parent b757471c81
commit fc6f621387
6 changed files with 267 additions and 255 deletions

View File

@ -2727,6 +2727,7 @@ filegroup(
"src/bigint/bigint-internal.cc", "src/bigint/bigint-internal.cc",
"src/bigint/bigint-internal.h", "src/bigint/bigint-internal.h",
"src/bigint/bigint.h", "src/bigint/bigint.h",
"src/bigint/bitwise.cc",
"src/bigint/digit-arithmetic.h", "src/bigint/digit-arithmetic.h",
"src/bigint/div-barrett.cc", "src/bigint/div-barrett.cc",
"src/bigint/div-burnikel.cc", "src/bigint/div-burnikel.cc",

View File

@ -5106,6 +5106,7 @@ v8_source_set("v8_bigint") {
"src/bigint/bigint-internal.cc", "src/bigint/bigint-internal.cc",
"src/bigint/bigint-internal.h", "src/bigint/bigint-internal.h",
"src/bigint/bigint.h", "src/bigint/bigint.h",
"src/bigint/bitwise.cc",
"src/bigint/digit-arithmetic.h", "src/bigint/digit-arithmetic.h",
"src/bigint/div-burnikel.cc", "src/bigint/div-burnikel.cc",
"src/bigint/div-helpers.cc", "src/bigint/div-helpers.cc",

View File

@ -227,12 +227,32 @@ void Add(RWDigits Z, Digits X, Digits Y);
// Addition of signed integers. Returns true if the result is negative. // Addition of signed integers. Returns true if the result is negative.
bool AddSigned(RWDigits Z, Digits X, bool x_negative, Digits Y, bool AddSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
bool y_negative); bool y_negative);
// Z := X + 1
void AddOne(RWDigits Z, Digits X);
// Z := X - Y. Requires X >= Y. // Z := X - Y. Requires X >= Y.
void Subtract(RWDigits Z, Digits X, Digits Y); void Subtract(RWDigits Z, Digits X, Digits Y);
// Subtraction of signed integers. Returns true if the result is negative. // Subtraction of signed integers. Returns true if the result is negative.
bool SubtractSigned(RWDigits Z, Digits X, bool x_negative, Digits Y, bool SubtractSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
bool y_negative); bool y_negative);
// Z := X - 1
void SubtractOne(RWDigits Z, Digits X);
// The bitwise operations assume that negative BigInts are represented as
// sign+magnitude. Their behavior depends on the sign of the inputs: negative
// inputs perform an implicit conversion to two's complement representation.
// Z := X & Y
void BitwiseAnd_PosPos(RWDigits Z, Digits X, Digits Y);
// Call this for a BigInt x = (magnitude=X, negative=true).
void BitwiseAnd_NegNeg(RWDigits Z, Digits X, Digits Y);
// Positive X, negative Y. Callers must swap arguments as needed.
void BitwiseAnd_PosNeg(RWDigits Z, Digits X, Digits Y);
void BitwiseOr_PosPos(RWDigits Z, Digits X, Digits Y);
void BitwiseOr_NegNeg(RWDigits Z, Digits X, Digits Y);
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);
enum class Status { kOk, kInterrupted }; enum class Status { kOk, kInterrupted };
@ -303,6 +323,28 @@ int ToStringResultLength(Digits X, int radix, bool sign);
// In DEBUG builds, the result of {ToString} will be initialized to this value. // In DEBUG builds, the result of {ToString} will be initialized to this value.
constexpr char kStringZapValue = '?'; constexpr char kStringZapValue = '?';
inline int BitwiseAnd_PosPos_ResultLength(int x_length, int y_length) {
return std::min(x_length, y_length);
}
inline int BitwiseAnd_NegNeg_ResultLength(int x_length, int y_length) {
// Result length growth example: -2 & -3 = -4 (2-bit inputs, 3-bit result).
return std::max(x_length, y_length) + 1;
}
inline int BitwiseAnd_PosNeg_ResultLength(int x_length) { return x_length; }
inline int BitwiseOrResultLength(int x_length, int y_length) {
return std::max(x_length, y_length);
}
inline int BitwiseXor_PosPos_ResultLength(int x_length, int y_length) {
return std::max(x_length, y_length);
}
inline int BitwiseXor_NegNeg_ResultLength(int x_length, int y_length) {
return std::max(x_length, y_length);
}
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;
}
// Support for parsing BigInts from Strings, using an Accumulator object // Support for parsing BigInts from Strings, using an Accumulator object
// for intermediate state. // for intermediate state.

136
src/bigint/bitwise.cc Normal file
View File

@ -0,0 +1,136 @@
// 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/bigint-internal.h"
#include "src/bigint/digit-arithmetic.h"
#include "src/bigint/vector-arithmetic.h"
namespace v8 {
namespace bigint {
void BitwiseAnd_PosPos(RWDigits Z, Digits X, Digits Y) {
int pairs = std::min(X.len(), Y.len());
DCHECK(Z.len() >= pairs);
int i = 0;
for (; i < pairs; i++) Z[i] = X[i] & Y[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
void BitwiseAnd_NegNeg(RWDigits Z, Digits X, Digits Y) {
// (-x) & (-y) == ~(x-1) & ~(y-1)
// == ~((x-1) | (y-1))
// == -(((x-1) | (y-1)) + 1)
int pairs = std::min(X.len(), Y.len());
digit_t x_borrow = 1;
digit_t y_borrow = 1;
int i = 0;
for (; i < pairs; i++) {
Z[i] = digit_sub(X[i], x_borrow, &x_borrow) |
digit_sub(Y[i], y_borrow, &y_borrow);
}
// (At least) one of the next two loops will perform zero iterations:
for (; i < X.len(); i++) Z[i] = digit_sub(X[i], x_borrow, &x_borrow);
for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], y_borrow, &y_borrow);
DCHECK(x_borrow == 0); // NOLINT(readability/check)
DCHECK(y_borrow == 0); // NOLINT(readability/check)
for (; i < Z.len(); i++) Z[i] = 0;
Add(Z, 1);
}
void BitwiseAnd_PosNeg(RWDigits Z, Digits X, Digits Y) {
// x & (-y) == x & ~(y-1)
int pairs = std::min(X.len(), Y.len());
digit_t borrow = 1;
int i = 0;
for (; i < pairs; i++) Z[i] = X[i] & ~digit_sub(Y[i], borrow, &borrow);
for (; i < X.len(); i++) Z[i] = X[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
void BitwiseOr_PosPos(RWDigits Z, Digits X, Digits Y) {
int pairs = std::min(X.len(), Y.len());
int i = 0;
for (; i < pairs; i++) Z[i] = X[i] | Y[i];
// (At least) one of the next two loops will perform zero iterations:
for (; i < X.len(); i++) Z[i] = X[i];
for (; i < Y.len(); i++) Z[i] = Y[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
void BitwiseOr_NegNeg(RWDigits Z, Digits X, Digits Y) {
// (-x) | (-y) == ~(x-1) | ~(y-1)
// == ~((x-1) & (y-1))
// == -(((x-1) & (y-1)) + 1)
int pairs = std::min(X.len(), Y.len());
digit_t x_borrow = 1;
digit_t y_borrow = 1;
int i = 0;
for (; i < pairs; i++) {
Z[i] = digit_sub(X[i], x_borrow, &x_borrow) &
digit_sub(Y[i], y_borrow, &y_borrow);
}
// Any leftover borrows don't matter, the '&' would drop them anyway.
for (; i < Z.len(); i++) Z[i] = 0;
Add(Z, 1);
}
void BitwiseOr_PosNeg(RWDigits Z, Digits X, Digits Y) {
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
int pairs = std::min(X.len(), Y.len());
digit_t borrow = 1;
int i = 0;
for (; i < pairs; i++) Z[i] = digit_sub(Y[i], borrow, &borrow) & ~X[i];
for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], borrow, &borrow);
DCHECK(borrow == 0); // NOLINT(readability/check)
for (; i < Z.len(); i++) Z[i] = 0;
Add(Z, 1);
}
void BitwiseXor_PosPos(RWDigits Z, Digits X, Digits Y) {
int pairs = X.len();
if (Y.len() < X.len()) {
std::swap(X, Y);
pairs = X.len();
}
DCHECK(X.len() <= Y.len());
int i = 0;
for (; i < pairs; i++) Z[i] = X[i] ^ Y[i];
for (; i < Y.len(); i++) Z[i] = Y[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
void BitwiseXor_NegNeg(RWDigits Z, Digits X, Digits Y) {
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
int pairs = std::min(X.len(), Y.len());
digit_t x_borrow = 1;
digit_t y_borrow = 1;
int i = 0;
for (; i < pairs; i++) {
Z[i] = digit_sub(X[i], x_borrow, &x_borrow) ^
digit_sub(Y[i], y_borrow, &y_borrow);
}
// (At least) one of the next two loops will perform zero iterations:
for (; i < X.len(); i++) Z[i] = digit_sub(X[i], x_borrow, &x_borrow);
for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], y_borrow, &y_borrow);
DCHECK(x_borrow == 0); // NOLINT(readability/check)
DCHECK(y_borrow == 0); // NOLINT(readability/check)
for (; i < Z.len(); i++) Z[i] = 0;
}
void BitwiseXor_PosNeg(RWDigits Z, Digits X, Digits Y) {
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
int pairs = std::min(X.len(), Y.len());
digit_t borrow = 1;
int i = 0;
for (; i < pairs; i++) Z[i] = X[i] ^ digit_sub(Y[i], borrow, &borrow);
// (At least) one of the next two loops will perform zero iterations:
for (; i < X.len(); i++) Z[i] = X[i];
for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], borrow, &borrow);
DCHECK(borrow == 0); // NOLINT(readability/check)
for (; i < Z.len(); i++) Z[i] = 0;
Add(Z, 1);
}
} // namespace bigint
} // namespace v8

View File

@ -118,5 +118,22 @@ bool SubtractSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
return !x_negative; return !x_negative;
} }
void AddOne(RWDigits Z, Digits X) {
digit_t carry = 1;
int i = 0;
for (; carry > 0 && i < X.len(); i++) Z[i] = digit_add2(X[i], carry, &carry);
if (carry > 0) Z[i++] = carry;
for (; i < X.len(); i++) Z[i] = X[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
void SubtractOne(RWDigits Z, Digits X) {
digit_t borrow = 1;
int i = 0;
for (; borrow > 0; i++) Z[i] = digit_sub(X[i], borrow, &borrow);
for (; i < X.len(); i++) Z[i] = X[i];
for (; i < Z.len(); i++) Z[i] = 0;
}
} // namespace bigint } // namespace bigint
} // namespace v8 } // namespace v8

View File

@ -81,16 +81,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
} }
// Internal helpers. // Internal helpers.
static MaybeHandle<MutableBigInt> BitwiseAnd(Isolate* isolate,
Handle<BigInt> x,
Handle<BigInt> y);
static MaybeHandle<MutableBigInt> BitwiseXor(Isolate* isolate,
Handle<BigInt> x,
Handle<BigInt> y);
static MaybeHandle<MutableBigInt> BitwiseOr(Isolate* isolate,
Handle<BigInt> x,
Handle<BigInt> y);
static Handle<BigInt> TruncateToNBits(Isolate* isolate, int n, static Handle<BigInt> TruncateToNBits(Isolate* isolate, int n,
Handle<BigInt> x); Handle<BigInt> x);
static Handle<BigInt> TruncateAndSubFromPowerOfTwo(Isolate* isolate, int n, static Handle<BigInt> TruncateAndSubFromPowerOfTwo(Isolate* isolate, int n,
@ -102,29 +92,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
MutableBigInt result_storage = MutableBigInt()); MutableBigInt result_storage = MutableBigInt());
static Handle<MutableBigInt> AbsoluteSubOne(Isolate* isolate, static Handle<MutableBigInt> AbsoluteSubOne(Isolate* isolate,
Handle<BigIntBase> x); Handle<BigIntBase> x);
static MaybeHandle<MutableBigInt> AbsoluteSubOne(Isolate* isolate,
Handle<BigIntBase> x,
int result_length);
enum ExtraDigitsHandling { kCopy, kSkip };
enum SymmetricOp { kSymmetric, kNotSymmetric };
static inline Handle<MutableBigInt> AbsoluteBitwiseOp(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage, ExtraDigitsHandling extra_digits,
SymmetricOp symmetric,
const std::function<digit_t(digit_t, digit_t)>& op);
static Handle<MutableBigInt> AbsoluteAnd(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage = MutableBigInt());
static Handle<MutableBigInt> AbsoluteAndNot(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage = MutableBigInt());
static Handle<MutableBigInt> AbsoluteOr(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage = MutableBigInt());
static Handle<MutableBigInt> AbsoluteXor(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage = MutableBigInt());
// Specialized helpers for shift operations. // Specialized helpers for shift operations.
static MaybeHandle<BigInt> LeftShiftByAbsolute(Isolate* isolate, static MaybeHandle<BigInt> LeftShiftByAbsolute(Isolate* isolate,
@ -146,7 +113,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
static uint64_t GetRawBits(BigIntBase x, bool* lossless); static uint64_t GetRawBits(BigIntBase x, bool* lossless);
// Digit arithmetic helpers. // Digit arithmetic helpers.
static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry);
static inline digit_t digit_sub(digit_t a, digit_t b, digit_t* borrow); static inline digit_t digit_sub(digit_t a, digit_t b, digit_t* borrow);
static inline bool digit_ismax(digit_t x) { static inline bool digit_ismax(digit_t x) {
return static_cast<digit_t>(~x) == 0; return static_cast<digit_t>(~x) == 0;
@ -406,7 +372,7 @@ MaybeHandle<BigInt> BigInt::BitwiseNot(Isolate* isolate, Handle<BigInt> x) {
MaybeHandle<MutableBigInt> result; MaybeHandle<MutableBigInt> result;
if (x->sign()) { if (x->sign()) {
// ~(-x) == ~(~(x-1)) == x-1 // ~(-x) == ~(~(x-1)) == x-1
result = MutableBigInt::AbsoluteSubOne(isolate, x, x->length()); result = MutableBigInt::AbsoluteSubOne(isolate, x);
} else { } else {
// ~x == -x-1 == -(x+1) // ~x == -x-1 == -(x+1)
result = MutableBigInt::AbsoluteAddOne(isolate, x, true); result = MutableBigInt::AbsoluteAddOne(isolate, x, true);
@ -673,96 +639,82 @@ bool BigInt::EqualToBigInt(BigInt x, BigInt y) {
MaybeHandle<BigInt> BigInt::BitwiseAnd(Isolate* isolate, Handle<BigInt> x, MaybeHandle<BigInt> BigInt::BitwiseAnd(Isolate* isolate, Handle<BigInt> x,
Handle<BigInt> y) { Handle<BigInt> y) {
return MutableBigInt::MakeImmutable(MutableBigInt::BitwiseAnd(isolate, x, y)); bool x_sign = x->sign();
} bool y_sign = y->sign();
Handle<MutableBigInt> result;
MaybeHandle<MutableBigInt> MutableBigInt::BitwiseAnd(Isolate* isolate, if (!x_sign && !y_sign) {
Handle<BigInt> x, int result_length =
Handle<BigInt> y) { bigint::BitwiseAnd_PosPos_ResultLength(x->length(), y->length());
if (!x->sign() && !y->sign()) { result = MutableBigInt::New(isolate, result_length).ToHandleChecked();
return AbsoluteAnd(isolate, x, y); bigint::BitwiseAnd_PosPos(GetRWDigits(result), GetDigits(x), GetDigits(y));
} else if (x->sign() && y->sign()) { DCHECK(!result->sign());
int result_length = std::max(x->length(), y->length()) + 1; } else if (x_sign && y_sign) {
// (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1)) int result_length =
// == -(((x-1) | (y-1)) + 1) bigint::BitwiseAnd_NegNeg_ResultLength(x->length(), y->length());
Handle<MutableBigInt> result; if (!MutableBigInt::New(isolate, result_length).ToHandle(&result)) {
if (!AbsoluteSubOne(isolate, x, result_length).ToHandle(&result)) { return {};
return MaybeHandle<MutableBigInt>();
} }
Handle<MutableBigInt> y_1 = AbsoluteSubOne(isolate, y); bigint::BitwiseAnd_NegNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
result = AbsoluteOr(isolate, result, y_1, *result); result->set_sign(true);
return AbsoluteAddOne(isolate, result, true, *result);
} else { } else {
DCHECK(x->sign() != y->sign()); if (x_sign) std::swap(x, y);
// Assume that x is the positive BigInt. int result_length = bigint::BitwiseAnd_PosNeg_ResultLength(x->length());
if (x->sign()) std::swap(x, y); result = MutableBigInt::New(isolate, result_length).ToHandleChecked();
// x & (-y) == x & ~(y-1) == x &~ (y-1) bigint::BitwiseAnd_PosNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
Handle<MutableBigInt> y_1 = AbsoluteSubOne(isolate, y); DCHECK(!result->sign());
return AbsoluteAndNot(isolate, x, y_1);
} }
return MutableBigInt::MakeImmutable(result);
} }
MaybeHandle<BigInt> BigInt::BitwiseXor(Isolate* isolate, Handle<BigInt> x, MaybeHandle<BigInt> BigInt::BitwiseXor(Isolate* isolate, Handle<BigInt> x,
Handle<BigInt> y) { Handle<BigInt> y) {
return MutableBigInt::MakeImmutable(MutableBigInt::BitwiseXor(isolate, x, y)); bool x_sign = x->sign();
} bool y_sign = y->sign();
Handle<MutableBigInt> result;
MaybeHandle<MutableBigInt> MutableBigInt::BitwiseXor(Isolate* isolate, if (!x_sign && !y_sign) {
Handle<BigInt> x, int result_length =
Handle<BigInt> y) { bigint::BitwiseXor_PosPos_ResultLength(x->length(), y->length());
if (!x->sign() && !y->sign()) { result = MutableBigInt::New(isolate, result_length).ToHandleChecked();
return AbsoluteXor(isolate, x, y); bigint::BitwiseXor_PosPos(GetRWDigits(result), GetDigits(x), GetDigits(y));
} else if (x->sign() && y->sign()) { DCHECK(!result->sign());
int result_length = std::max(x->length(), y->length()); } else if (x_sign && y_sign) {
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1) int result_length =
Handle<MutableBigInt> result = bigint::BitwiseXor_NegNeg_ResultLength(x->length(), y->length());
AbsoluteSubOne(isolate, x, result_length).ToHandleChecked(); result = MutableBigInt::New(isolate, result_length).ToHandleChecked();
Handle<MutableBigInt> y_1 = AbsoluteSubOne(isolate, y); bigint::BitwiseXor_NegNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
return AbsoluteXor(isolate, result, y_1, *result); DCHECK(!result->sign());
} else { } else {
DCHECK(x->sign() != y->sign()); if (x_sign) std::swap(x, y);
int result_length = std::max(x->length(), y->length()) + 1; int result_length =
// Assume that x is the positive BigInt. bigint::BitwiseXor_PosNeg_ResultLength(x->length(), y->length());
if (x->sign()) std::swap(x, y); if (!MutableBigInt::New(isolate, result_length).ToHandle(&result)) {
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1) return {};
Handle<MutableBigInt> result;
if (!AbsoluteSubOne(isolate, y, result_length).ToHandle(&result)) {
return MaybeHandle<MutableBigInt>();
} }
result = AbsoluteXor(isolate, result, x, *result); bigint::BitwiseXor_PosNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
return AbsoluteAddOne(isolate, result, true, *result); result->set_sign(true);
} }
return MutableBigInt::MakeImmutable(result);
} }
MaybeHandle<BigInt> BigInt::BitwiseOr(Isolate* isolate, Handle<BigInt> x, MaybeHandle<BigInt> BigInt::BitwiseOr(Isolate* isolate, Handle<BigInt> x,
Handle<BigInt> y) { Handle<BigInt> y) {
return MutableBigInt::MakeImmutable(MutableBigInt::BitwiseOr(isolate, x, y)); bool x_sign = x->sign();
} bool y_sign = y->sign();
int result_length = bigint::BitwiseOrResultLength(x->length(), y->length());
MaybeHandle<MutableBigInt> MutableBigInt::BitwiseOr(Isolate* isolate, Handle<MutableBigInt> result =
Handle<BigInt> x, MutableBigInt::New(isolate, result_length).ToHandleChecked();
Handle<BigInt> y) { if (!x_sign && !y_sign) {
int result_length = std::max(x->length(), y->length()); bigint::BitwiseOr_PosPos(GetRWDigits(result), GetDigits(x), GetDigits(y));
if (!x->sign() && !y->sign()) { DCHECK(!result->sign());
return AbsoluteOr(isolate, x, y); } else if (x_sign && y_sign) {
} else if (x->sign() && y->sign()) { bigint::BitwiseOr_NegNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1)) result->set_sign(true);
// == -(((x-1) & (y-1)) + 1)
Handle<MutableBigInt> result =
AbsoluteSubOne(isolate, x, result_length).ToHandleChecked();
Handle<MutableBigInt> y_1 = AbsoluteSubOne(isolate, y);
result = AbsoluteAnd(isolate, result, y_1, *result);
return AbsoluteAddOne(isolate, result, true, *result);
} else { } else {
DCHECK(x->sign() != y->sign()); if (x_sign) std::swap(x, y);
// Assume that x is the positive BigInt. bigint::BitwiseOr_PosNeg(GetRWDigits(result), GetDigits(x), GetDigits(y));
if (x->sign()) std::swap(x, y); result->set_sign(true);
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
Handle<MutableBigInt> result =
AbsoluteSubOne(isolate, y, result_length).ToHandleChecked();
result = AbsoluteAndNot(isolate, result, x, *result);
return AbsoluteAddOne(isolate, result, true, *result);
} }
return MutableBigInt::MakeImmutable(result);
} }
MaybeHandle<BigInt> BigInt::Increment(Isolate* isolate, Handle<BigInt> x) { MaybeHandle<BigInt> BigInt::Increment(Isolate* isolate, Handle<BigInt> x) {
@ -1270,16 +1222,12 @@ MaybeHandle<MutableBigInt> MutableBigInt::AbsoluteAddOne(
} else { } else {
DCHECK(result->length() == result_length); DCHECK(result->length() == result_length);
} }
digit_t carry = 1; if (input_length == 0) {
for (int i = 0; i < input_length; i++) { result->set_digit(0, 1);
digit_t new_carry = 0; } else if (input_length == 1 && !will_overflow) {
result->set_digit(i, digit_add(x->digit(i), carry, &new_carry)); result->set_digit(0, x->digit(0) + 1);
carry = new_carry;
}
if (result_length > input_length) {
result->set_digit(input_length, carry);
} else { } else {
DCHECK_EQ(carry, 0); bigint::AddOne(GetRWDigits(result), GetDigits(x));
} }
result->set_sign(sign); result->set_sign(sign);
return result; return result;
@ -1289,134 +1237,16 @@ MaybeHandle<MutableBigInt> MutableBigInt::AbsoluteAddOne(
Handle<MutableBigInt> MutableBigInt::AbsoluteSubOne(Isolate* isolate, Handle<MutableBigInt> MutableBigInt::AbsoluteSubOne(Isolate* isolate,
Handle<BigIntBase> x) { Handle<BigIntBase> x) {
DCHECK(!x->is_zero()); DCHECK(!x->is_zero());
// Requesting a result length identical to an existing BigInt's length
// cannot overflow the limit.
return AbsoluteSubOne(isolate, x, x->length()).ToHandleChecked();
}
// Like the above, but you can specify that the allocated result should have
// length {result_length}, which must be at least as large as {x->length()}.
MaybeHandle<MutableBigInt> MutableBigInt::AbsoluteSubOne(Isolate* isolate,
Handle<BigIntBase> x,
int result_length) {
DCHECK(!x->is_zero());
DCHECK(result_length >= x->length());
Handle<MutableBigInt> result;
if (!New(isolate, result_length).ToHandle(&result)) {
return MaybeHandle<MutableBigInt>();
}
int length = x->length(); int length = x->length();
digit_t borrow = 1; Handle<MutableBigInt> result = New(isolate, length).ToHandleChecked();
for (int i = 0; i < length; i++) { if (length == 1) {
digit_t new_borrow = 0; result->set_digit(0, x->digit(0) - 1);
result->set_digit(i, digit_sub(x->digit(i), borrow, &new_borrow));
borrow = new_borrow;
}
DCHECK_EQ(borrow, 0);
for (int i = length; i < result_length; i++) {
result->set_digit(i, borrow);
}
return result;
}
// Helper for Absolute{And,AndNot,Or,Xor}.
// Performs the given binary {op} on digit pairs of {x} and {y}; when the
// end of the shorter of the two is reached, {extra_digits} configures how
// remaining digits in the longer input (if {symmetric} == kSymmetric, in
// {x} otherwise) are handled: copied to the result or ignored.
// If {result_storage} is non-nullptr, it will be used for the result and
// any extra digits in it will be zeroed out, otherwise a new BigInt (with
// the same length as the longer input) will be allocated.
// {result_storage} may alias {x} or {y} for in-place modification.
// Example:
// y: [ y2 ][ y1 ][ y0 ]
// x: [ x3 ][ x2 ][ x1 ][ x0 ]
// | | | |
// (kCopy) (op) (op) (op)
// | | | |
// v v v v
// result_storage: [ 0 ][ x3 ][ r2 ][ r1 ][ r0 ]
inline Handle<MutableBigInt> MutableBigInt::AbsoluteBitwiseOp(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage, ExtraDigitsHandling extra_digits,
SymmetricOp symmetric, const std::function<digit_t(digit_t, digit_t)>& op) {
int x_length = x->length();
int y_length = y->length();
int num_pairs = y_length;
if (x_length < y_length) {
num_pairs = x_length;
if (symmetric == kSymmetric) {
std::swap(x, y);
std::swap(x_length, y_length);
}
}
DCHECK(num_pairs == std::min(x_length, y_length));
Handle<MutableBigInt> result(result_storage, isolate);
int result_length = extra_digits == kCopy ? x_length : num_pairs;
if (result_storage.is_null()) {
result = New(isolate, result_length).ToHandleChecked();
} else { } else {
DCHECK(result_storage.length() >= result_length); bigint::SubtractOne(GetRWDigits(result), GetDigits(x));
result_length = result_storage.length();
}
int i = 0;
for (; i < num_pairs; i++) {
result->set_digit(i, op(x->digit(i), y->digit(i)));
}
if (extra_digits == kCopy) {
for (; i < x_length; i++) {
result->set_digit(i, x->digit(i));
}
}
for (; i < result_length; i++) {
result->set_digit(i, 0);
} }
return result; return result;
} }
// If {result_storage} is non-nullptr, it will be used for the result,
// otherwise a new BigInt of appropriate length will be allocated.
// {result_storage} may alias {x} or {y} for in-place modification.
Handle<MutableBigInt> MutableBigInt::AbsoluteAnd(Isolate* isolate,
Handle<BigIntBase> x,
Handle<BigIntBase> y,
MutableBigInt result_storage) {
return AbsoluteBitwiseOp(isolate, x, y, result_storage, kSkip, kSymmetric,
[](digit_t a, digit_t b) { return a & b; });
}
// If {result_storage} is non-nullptr, it will be used for the result,
// otherwise a new BigInt of appropriate length will be allocated.
// {result_storage} may alias {x} or {y} for in-place modification.
Handle<MutableBigInt> MutableBigInt::AbsoluteAndNot(
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage) {
return AbsoluteBitwiseOp(isolate, x, y, result_storage, kCopy, kNotSymmetric,
[](digit_t a, digit_t b) { return a & ~b; });
}
// If {result_storage} is non-nullptr, it will be used for the result,
// otherwise a new BigInt of appropriate length will be allocated.
// {result_storage} may alias {x} or {y} for in-place modification.
Handle<MutableBigInt> MutableBigInt::AbsoluteOr(Isolate* isolate,
Handle<BigIntBase> x,
Handle<BigIntBase> y,
MutableBigInt result_storage) {
return AbsoluteBitwiseOp(isolate, x, y, result_storage, kCopy, kSymmetric,
[](digit_t a, digit_t b) { return a | b; });
}
// If {result_storage} is non-nullptr, it will be used for the result,
// otherwise a new BigInt of appropriate length will be allocated.
// {result_storage} may alias {x} or {y} for in-place modification.
Handle<MutableBigInt> MutableBigInt::AbsoluteXor(Isolate* isolate,
Handle<BigIntBase> x,
Handle<BigIntBase> y,
MutableBigInt result_storage) {
return AbsoluteBitwiseOp(isolate, x, y, result_storage, kCopy, kSymmetric,
[](digit_t a, digit_t b) { return a ^ b; });
}
MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate, MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
Handle<BigIntBase> x, Handle<BigIntBase> x,
Handle<BigIntBase> y) { Handle<BigIntBase> y) {
@ -1950,21 +1780,6 @@ using twodigit_t = uint64_t;
using twodigit_t = __uint128_t; using twodigit_t = __uint128_t;
#endif #endif
// {carry} must point to an initialized digit_t and will either be incremented
// by one or left alone.
inline BigInt::digit_t MutableBigInt::digit_add(digit_t a, digit_t b,
digit_t* carry) {
#if HAVE_TWODIGIT_T
twodigit_t result = static_cast<twodigit_t>(a) + static_cast<twodigit_t>(b);
*carry += result >> kDigitBits;
return static_cast<digit_t>(result);
#else
digit_t result = a + b;
if (result < a) *carry += 1;
return result;
#endif
}
// {borrow} must point to an initialized digit_t and will either be incremented // {borrow} must point to an initialized digit_t and will either be incremented
// by one or left alone. // by one or left alone.
inline BigInt::digit_t MutableBigInt::digit_sub(digit_t a, digit_t b, inline BigInt::digit_t MutableBigInt::digit_sub(digit_t a, digit_t b,