diff options
Diffstat (limited to 'deps/v8/src/bigint/bitwise.cc')
-rw-r--r-- | deps/v8/src/bigint/bitwise.cc | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/deps/v8/src/bigint/bitwise.cc b/deps/v8/src/bigint/bitwise.cc new file mode 100644 index 0000000000..087847c118 --- /dev/null +++ b/deps/v8/src/bigint/bitwise.cc @@ -0,0 +1,262 @@ +// 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/util.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 { + +// Z := (least significant n bits of X). +void TruncateToNBits(RWDigits Z, Digits X, int n) { + int digits = DIV_CEIL(n, kDigitBits); + int bits = n % kDigitBits; + // Copy all digits except the MSD. + int last = digits - 1; + for (int i = 0; i < last; i++) { + Z[i] = X[i]; + } + // The MSD might contain extra bits that we don't want. + digit_t msd = X[last]; + if (bits != 0) { + int drop = kDigitBits - bits; + msd = (msd << drop) >> drop; + } + Z[last] = msd; +} + +// Z := 2**n - (least significant n bits of X). +void TruncateAndSubFromPowerOfTwo(RWDigits Z, Digits X, int n) { + int digits = DIV_CEIL(n, kDigitBits); + int bits = n % kDigitBits; + // Process all digits except the MSD. Take X's digits, then simulate leading + // zeroes. + int last = digits - 1; + int have_x = std::min(last, X.len()); + digit_t borrow = 0; + int i = 0; + for (; i < have_x; i++) Z[i] = digit_sub2(0, X[i], borrow, &borrow); + for (; i < last; i++) Z[i] = digit_sub(0, borrow, &borrow); + + // The MSD might contain extra bits that we don't want. + digit_t msd = last < X.len() ? X[last] : 0; + if (bits == 0) { + Z[last] = digit_sub2(0, msd, borrow, &borrow); + } else { + int drop = kDigitBits - bits; + msd = (msd << drop) >> drop; + digit_t minuend_msd = static_cast<digit_t>(1) << bits; + digit_t result_msd = digit_sub2(minuend_msd, msd, borrow, &borrow); + DCHECK(borrow == 0); // result < 2^n. NOLINT(readability/check) + // If all subtracted bits were zero, we have to get rid of the + // materialized minuend_msd again. + Z[last] = result_msd & (minuend_msd - 1); + } +} + +} // namespace + +// Returns -1 when the operation would return X unchanged. +int AsIntNResultLength(Digits X, bool x_negative, int n) { + int needed_digits = DIV_CEIL(n, kDigitBits); + // Generally: decide based on number of digits, and bits in the top digit. + if (X.len() < needed_digits) return -1; + if (X.len() > needed_digits) return needed_digits; + digit_t top_digit = X[needed_digits - 1]; + digit_t compare_digit = digit_t{1} << ((n - 1) % kDigitBits); + if (top_digit < compare_digit) return -1; + if (top_digit > compare_digit) return needed_digits; + // Special case: if X == -2**(n-1), truncation is a no-op. + if (!x_negative) return needed_digits; + for (int i = needed_digits - 2; i >= 0; i--) { + if (X[i] != 0) return needed_digits; + } + return -1; +} + +bool AsIntN(RWDigits Z, Digits X, bool x_negative, int n) { + DCHECK(X.len() > 0); // NOLINT(readability/check) + DCHECK(n > 0); // NOLINT(readability/check) + // NOLINTNEXTLINE(readability/check) + DCHECK(AsIntNResultLength(X, x_negative, n) > 0); + int needed_digits = DIV_CEIL(n, kDigitBits); + digit_t top_digit = X[needed_digits - 1]; + digit_t compare_digit = digit_t{1} << ((n - 1) % kDigitBits); + // The canonical algorithm would be: convert negative numbers to two's + // complement representation, truncate, convert back to sign+magnitude. To + // avoid the conversions, we predict what the result would be: + // When the (n-1)th bit is not set: + // - truncate the absolute value + // - preserve the sign. + // When the (n-1)th bit is set: + // - subtract the truncated absolute value from 2**n to simulate two's + // complement representation + // - flip the sign, unless it's the special case where the input is negative + // and the result is the minimum n-bit integer. E.g. asIntN(3, -12) => -4. + bool has_bit = (top_digit & compare_digit) == compare_digit; + if (!has_bit) { + TruncateToNBits(Z, X, n); + return x_negative; + } + TruncateAndSubFromPowerOfTwo(Z, X, n); + if (!x_negative) return true; // Result is negative. + // Scan for the special case (see above): if all bits below the (n-1)th + // digit are zero, the result is negative. + if ((top_digit & (compare_digit - 1)) != 0) return false; + for (int i = needed_digits - 2; i >= 0; i--) { + if (X[i] != 0) return false; + } + return true; +} + +// Returns -1 when the operation would return X unchanged. +int AsUintN_Pos_ResultLength(Digits X, int n) { + int needed_digits = DIV_CEIL(n, kDigitBits); + if (X.len() < needed_digits) return -1; + if (X.len() > needed_digits) return needed_digits; + int bits_in_top_digit = n % kDigitBits; + if (bits_in_top_digit == 0) return -1; + digit_t top_digit = X[needed_digits - 1]; + if ((top_digit >> bits_in_top_digit) == 0) return -1; + return needed_digits; +} + +void AsUintN_Pos(RWDigits Z, Digits X, int n) { + DCHECK(AsUintN_Pos_ResultLength(X, n) > 0); // NOLINT(readability/check) + TruncateToNBits(Z, X, n); +} + +void AsUintN_Neg(RWDigits Z, Digits X, int n) { + TruncateAndSubFromPowerOfTwo(Z, X, n); +} + +} // namespace bigint +} // namespace v8 |