[bigint] Move toString conversion to src/bigint/

This just moves the existing algorithm, and translates it from
Handle<BigInt> to Digits as underlying data format.

Bug: v8:11515
Change-Id: Ieefee4e953e14f4c574aebab94d825ddb7c31f8c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2975304
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75391}
This commit is contained in:
Jakob Kummerow 2021-06-25 16:56:24 +02:00 committed by V8 LUCI CQ
parent 3f62253a4b
commit ee307c747f
9 changed files with 379 additions and 354 deletions

View File

@ -2433,6 +2433,7 @@ filegroup(
"src/bigint/div-schoolbook.cc",
"src/bigint/mul-karatsuba.cc",
"src/bigint/mul-schoolbook.cc",
"src/bigint/tostring.cc",
"src/bigint/util.h",
"src/bigint/vector-arithmetic.cc",
"src/bigint/vector-arithmetic.h",

View File

@ -4949,6 +4949,7 @@ v8_source_set("v8_bigint") {
"src/bigint/div-schoolbook.cc",
"src/bigint/mul-karatsuba.cc",
"src/bigint/mul-schoolbook.cc",
"src/bigint/tostring.cc",
"src/bigint/util.h",
"src/bigint/vector-arithmetic.cc",
"src/bigint/vector-arithmetic.h",

View File

@ -99,5 +99,12 @@ Status Processor::Modulo(RWDigits R, Digits A, Digits B) {
return impl->get_and_clear_status();
}
Status Processor::ToString(char* out, int* out_length, Digits X, int radix,
bool sign) {
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
impl->ToString(out, out_length, X, radix, sign);
return impl->get_and_clear_status();
}
} // namespace bigint
} // namespace v8

View File

@ -38,9 +38,12 @@ class ProcessorImpl : public Processor {
void Modulo(RWDigits R, Digits A, Digits B);
// {out_length} initially contains the allocated capacity of {out}, and
// upon return will be set to the actual length of the result string.
void ToString(char* out, int* out_length, Digits X, int radix, bool sign);
bool should_terminate() { return status_ == Status::kInterrupted; }
private:
// Each unit is supposed to represent approximately one CPU {mul} instruction.
// Doesn't need to be accurate; we just want to make sure to check for
// interrupt requests every now and then (roughly every 10-100 ms; often
@ -58,6 +61,7 @@ class ProcessorImpl : public Processor {
}
}
private:
uintptr_t work_estimate_{0};
Status status_{Status::kOk};
Platform* platform_;

View File

@ -240,6 +240,10 @@ class Processor {
Status Divide(RWDigits Q, Digits A, Digits B);
// R := A % B
Status Modulo(RWDigits R, Digits A, Digits B);
// {out_length} initially contains the allocated capacity of {out}, and
// upon return will be set to the actual length of the result string.
Status ToString(char* out, int* out_length, Digits X, int radix, bool sign);
};
inline int MultiplyResultLength(Digits X, Digits Y) {
@ -250,6 +254,10 @@ inline int DivideResultLength(Digits A, Digits B) {
}
inline int ModuloResultLength(Digits B) { return B.len(); }
int ToStringResultLength(Digits X, int radix, bool sign);
// In DEBUG builds, the result of {ToString} will be initialized to this value.
constexpr char kStringZapValue = '?';
} // namespace bigint
} // namespace v8

296
src/bigint/tostring.cc Normal file
View File

@ -0,0 +1,296 @@
// 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 <cstring>
#include <limits>
#include "src/bigint/bigint-internal.h"
#include "src/bigint/digit-arithmetic.h"
#include "src/bigint/div-helpers.h"
#include "src/bigint/util.h"
#include "src/bigint/vector-arithmetic.h"
namespace v8 {
namespace bigint {
namespace {
// Lookup table for the maximum number of bits required per character of a
// base-N string representation of a number. To increase accuracy, the array
// value is the actual value multiplied by 32. To generate this table:
// for (var i = 0; i <= 36; i++) { print(Math.ceil(Math.log2(i) * 32) + ","); }
constexpr uint8_t kMaxBitsPerChar[] = {
0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8
102, 107, 111, 115, 119, 122, 126, 128, // 9..16
131, 134, 136, 139, 141, 143, 145, 147, // 17..24
149, 151, 153, 154, 156, 158, 159, 160, // 25..32
162, 163, 165, 166, // 33..36
};
static const int kBitsPerCharTableShift = 5;
static const size_t kBitsPerCharTableMultiplier = 1u << kBitsPerCharTableShift;
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// Raises {base} to the power of {exponent}. Does not check for overflow.
digit_t digit_pow(digit_t base, digit_t exponent) {
digit_t result = 1ull;
while (exponent > 0) {
if (exponent & 1) {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
// Compile-time version of the above.
constexpr digit_t digit_pow_rec(digit_t base, digit_t exponent) {
return exponent == 1 ? base : base * digit_pow_rec(base, exponent - 1);
}
// A variant of ToStringFormatter::BasecaseLast, specialized for a radix
// known at compile-time.
template <int radix>
char* BasecaseFixedLast(digit_t chunk, char* out) {
while (chunk != 0) {
DCHECK(*(out - 1) == kStringZapValue); // NOLINT(readability/check)
if (radix <= 10) {
*(--out) = '0' + (chunk % radix);
} else {
*(--out) = kConversionChars[chunk % radix];
}
chunk /= radix;
}
return out;
}
// By making {radix} a compile-time constant and computing {chunk_divisor}
// as another compile-time constant from it, we allow the compiler to emit
// an optimized instruction sequence based on multiplications with "magic"
// numbers (modular multiplicative inverses) instead of actual divisions.
// The price we pay is having to work on half digits; the technique doesn't
// work with twodigit_t-by-digit_t divisions.
// Includes an equivalent of ToStringFormatter::BasecaseMiddle, accordingly
// specialized for a radix known at compile time.
template <digit_t radix>
char* DivideByMagic(RWDigits rest, Digits input, char* output) {
constexpr uint8_t max_bits_per_char = kMaxBitsPerChar[radix];
constexpr int chunk_chars =
kHalfDigitBits * kBitsPerCharTableMultiplier / max_bits_per_char;
constexpr digit_t chunk_divisor = digit_pow_rec(radix, chunk_chars);
digit_t remainder = 0;
for (int i = input.len() - 1; i >= 0; i--) {
digit_t d = input[i];
digit_t upper = (remainder << kHalfDigitBits) | (d >> kHalfDigitBits);
digit_t u_result = upper / chunk_divisor;
remainder = upper % chunk_divisor;
digit_t lower = (remainder << kHalfDigitBits) | (d & kHalfDigitMask);
digit_t l_result = lower / chunk_divisor;
remainder = lower % chunk_divisor;
rest[i] = (u_result << kHalfDigitBits) | l_result;
}
// {remainder} is now the current chunk to be written out.
for (int i = 0; i < chunk_chars; i++) {
DCHECK(*(output - 1) == kStringZapValue); // NOLINT(readability/check)
if (radix <= 10) {
*(--output) = '0' + (remainder % radix);
} else {
*(--output) = kConversionChars[remainder % radix];
}
remainder /= radix;
}
DCHECK(remainder == 0); // NOLINT(readability/check)
return output;
}
class ToStringFormatter {
public:
ToStringFormatter(Digits X, int radix, bool sign, char* out,
int chars_available, ProcessorImpl* processor)
: digits_(X),
radix_(radix),
sign_(sign),
out_start_(out),
out_end_(out + chars_available),
out_(out_end_),
processor_(processor) {
DCHECK(chars_available >= ToStringResultLength(digits_, radix_, sign_));
}
void Start();
int Finish();
void Classic() {
if (digits_.len() == 0) {
*(--out_) = '0';
return;
}
if (digits_.len() == 1) {
out_ = BasecaseLast(digits_[0], out_);
return;
}
// {rest} holds the part of the BigInt that we haven't looked at yet.
// Not to be confused with "remainder"!
ScratchDigits rest(digits_.len());
// In the first round, divide the input, allocating a new BigInt for
// the result == rest; from then on divide the rest in-place.
Digits dividend = digits_;
do {
if (radix_ == 10) {
// Faster but costs binary size, so we optimize the most common case.
out_ = DivideByMagic<10>(rest, dividend, out_);
processor_->AddWorkEstimate(rest.len() * 2);
} else {
digit_t chunk;
processor_->DivideSingle(rest, &chunk, dividend, chunk_divisor_);
out_ = BasecaseMiddle(chunk, out_);
// Assume that a division is about ten times as expensive as a
// multiplication.
processor_->AddWorkEstimate(rest.len() * 10);
}
if (processor_->should_terminate()) return;
rest.Normalize();
dividend = rest;
} while (rest.len() > 1);
out_ = BasecaseLast(rest[0], out_);
}
void BasePowerOfTwo();
private:
// When processing the last (most significant) digit, don't write leading
// zeros.
char* BasecaseLast(digit_t digit, char* out) {
if (radix_ == 10) return BasecaseFixedLast<10>(digit, out);
do {
DCHECK(*(out - 1) == kStringZapValue); // NOLINT(readability/check)
*(--out) = kConversionChars[digit % radix_];
digit /= radix_;
} while (digit > 0);
return out;
}
// When processing a middle (non-most significant) digit, always write the
// same number of characters (as many '0' as necessary).
char* BasecaseMiddle(digit_t digit, char* out) {
for (int i = 0; i < chunk_chars_; i++) {
DCHECK(*(out - 1) == kStringZapValue); // NOLINT(readability/check)
*(--out) = kConversionChars[digit % radix_];
digit /= radix_;
}
DCHECK(digit == 0); // NOLINT(readability/check)
return out;
}
Digits digits_;
int radix_;
int max_bits_per_char_ = 0;
int chunk_chars_ = 0;
bool sign_;
char* out_start_;
char* out_end_;
char* out_;
digit_t chunk_divisor_ = 0;
ProcessorImpl* processor_;
};
// Prepares data for {Classic}. Not needed for {BasePowerOfTwo}.
void ToStringFormatter::Start() {
max_bits_per_char_ = kMaxBitsPerChar[radix_];
chunk_chars_ = kDigitBits * kBitsPerCharTableMultiplier / max_bits_per_char_;
chunk_divisor_ = digit_pow(radix_, chunk_chars_);
// By construction of chunk_chars_, there can't have been overflow.
DCHECK(chunk_divisor_ != 0); // NOLINT(readability/check)
}
int ToStringFormatter::Finish() {
DCHECK(out_ >= out_start_);
DCHECK(out_ < out_end_); // At least one character was written.
while (out_ < out_end_ && *out_ == '0') out_++;
if (sign_) *(--out_) = '-';
int excess = 0;
if (out_ > out_start_) {
size_t actual_length = out_end_ - out_;
excess = static_cast<int>(out_ - out_start_);
std::memmove(out_start_, out_, actual_length);
}
return excess;
}
void ToStringFormatter::BasePowerOfTwo() {
const int bits_per_char = CountTrailingZeros(radix_);
const int char_mask = radix_ - 1;
digit_t digit = 0;
// Keeps track of how many unprocessed bits there are in {digit}.
int available_bits = 0;
for (int i = 0; i < digits_.len() - 1; i++) {
digit_t new_digit = digits_[i];
// Take any leftover bits from the last iteration into account.
int current = (digit | (new_digit << available_bits)) & char_mask;
*(--out_) = kConversionChars[current];
int consumed_bits = bits_per_char - available_bits;
digit = new_digit >> consumed_bits;
available_bits = kDigitBits - consumed_bits;
while (available_bits >= bits_per_char) {
*(--out_) = kConversionChars[digit & char_mask];
digit >>= bits_per_char;
available_bits -= bits_per_char;
}
}
// Take any leftover bits from the last iteration into account.
digit_t msd = digits_.msd();
int current = (digit | (msd << available_bits)) & char_mask;
*(--out_) = kConversionChars[current];
digit = msd >> (bits_per_char - available_bits);
while (digit != 0) {
*(--out_) = kConversionChars[digit & char_mask];
digit >>= bits_per_char;
}
}
} // namespace
void ProcessorImpl::ToString(char* out, int* out_length, Digits X, int radix,
bool sign) {
#if DEBUG
for (int i = 0; i < *out_length; i++) out[i] = kStringZapValue;
#endif
ToStringFormatter formatter(X, radix, sign, out, *out_length, this);
if (IsPowerOfTwo(radix)) {
formatter.BasePowerOfTwo();
} else {
formatter.Start();
formatter.Classic();
}
int excess = formatter.Finish();
*out_length -= excess;
}
int ToStringResultLength(Digits X, int radix, bool sign) {
const int bit_length = BitLength(X);
int result;
if (IsPowerOfTwo(radix)) {
const int bits_per_char = CountTrailingZeros(radix);
result = DIV_CEIL(bit_length, bits_per_char) + sign;
} else {
// Maximum number of bits we can represent with one character.
const uint8_t max_bits_per_char = kMaxBitsPerChar[radix];
// For estimating the result length, we have to be pessimistic and work with
// the minimum number of bits one character can represent.
const uint8_t min_bits_per_char = max_bits_per_char - 1;
// Perform the following computation with uint64_t to avoid overflows.
uint64_t chars_required = bit_length;
chars_required *= kBitsPerCharTableMultiplier;
chars_required = DIV_CEIL(chars_required, min_bits_per_char);
DCHECK(chars_required < std::numeric_limits<int>::max());
result = static_cast<int>(chars_required);
}
result += sign;
return result;
}
} // namespace bigint
} // namespace v8

View File

@ -50,10 +50,25 @@ constexpr int CountLeadingZeros(uint32_t value) {
#endif
}
inline constexpr int CountTrailingZeros(uint32_t value) {
#if __GNUC__ || __clang__
return value == 0 ? 32 : __builtin_ctz(value);
#elif _MSC_VER
unsigned long index = 0; // NOLINT(runtime/int).
return _BitScanForward(&index, value) ? index : 32;
#else
#error Unsupported compiler.
#endif
}
inline constexpr int BitLength(int n) {
return 32 - CountLeadingZeros(static_cast<uint32_t>(n));
}
inline constexpr bool IsPowerOfTwo(int value) {
return value > 0 && (value & (value - 1)) == 0;
}
} // namespace bigint
} // namespace v8

View File

@ -56,6 +56,10 @@ inline bool GreaterThanOrEqual(Digits A, Digits B) {
return Compare(A, B) >= 0;
}
inline int BitLength(Digits X) {
return X.len() * kDigitBits - CountLeadingZeros(X.msd());
}
} // namespace bigint
} // namespace v8

View File

@ -139,11 +139,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
digit_t summand, int n, MutableBigInt result);
void InplaceMultiplyAdd(uintptr_t factor, uintptr_t summand);
// Specialized helpers for Divide/Remainder.
static void AbsoluteDivSmall(Isolate* isolate, Handle<BigIntBase> x,
digit_t divisor, Handle<MutableBigInt>* quotient,
digit_t* remainder);
// Specialized helpers for shift operations.
static MaybeHandle<BigInt> LeftShiftByAbsolute(Isolate* isolate,
Handle<BigIntBase> x,
@ -154,14 +149,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
static Handle<BigInt> RightShiftByMaximum(Isolate* isolate, bool sign);
static Maybe<digit_t> ToShiftAmount(Handle<BigIntBase> x);
static MaybeHandle<String> ToStringBasePowerOfTwo(Isolate* isolate,
Handle<BigIntBase> x,
int radix,
ShouldThrow should_throw);
static MaybeHandle<String> ToStringGeneric(Isolate* isolate,
Handle<BigIntBase> x, int radix,
ShouldThrow should_throw);
static double ToDouble(Handle<BigIntBase> x);
enum Rounding { kRoundDown, kTie, kRoundUp };
static Rounding DecideRounding(Handle<BigIntBase> x, int mantissa_bits_unset,
@ -175,9 +162,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
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_mul(digit_t a, digit_t b, digit_t* high);
static inline digit_t digit_div(digit_t high, digit_t low, digit_t divisor,
digit_t* remainder);
static digit_t digit_pow(digit_t base, digit_t exponent);
static inline bool digit_ismax(digit_t x) {
return static_cast<digit_t>(~x) == 0;
}
@ -1004,11 +988,49 @@ MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint,
if (bigint->is_zero()) {
return isolate->factory()->zero_string();
}
if (base::bits::IsPowerOfTwo(radix)) {
return MutableBigInt::ToStringBasePowerOfTwo(isolate, bigint, radix,
should_throw);
DCHECK(radix >= 2 && radix <= 36);
const bool sign = bigint->sign();
int size = bigint::ToStringResultLength(GetDigits(bigint), radix, sign);
if (size > String::kMaxLength) {
if (should_throw == kThrowOnError) {
THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String);
} else {
return {};
}
}
return MutableBigInt::ToStringGeneric(isolate, bigint, radix, should_throw);
Handle<SeqOneByteString> result =
isolate->factory()->NewRawOneByteString(size).ToHandleChecked();
int allocated_size = size;
DisallowGarbageCollection no_gc;
char* characters = reinterpret_cast<char*>(result->GetChars(no_gc));
bigint::Status status = isolate->bigint_processor()->ToString(
characters, &size, GetDigits(bigint), radix, sign);
if (status == bigint::Status::kInterrupted) {
AllowGarbageCollection terminating_anyway;
isolate->TerminateExecution();
return {};
}
// Rigth-trim any over-allocation (which can happen due to conservative
// estimates).
if (size < allocated_size) {
result->set_length(size, kReleaseStore);
int string_size = SeqOneByteString::SizeFor(allocated_size);
int needed_size = SeqOneByteString::SizeFor(size);
if (needed_size < string_size && !isolate->heap()->IsLargeObject(*result)) {
Address new_end = result->address() + needed_size;
isolate->heap()->CreateFillerObjectAt(
new_end, (string_size - needed_size), ClearRecordedSlots::kNo);
}
}
#if DEBUG
// Verify that all characters have been written.
DCHECK(result->length() == size);
for (int i = 0; i < size; i++) {
DCHECK_NE(characters[i], bigint::kStringZapValue);
}
#endif
return result;
}
MaybeHandle<BigInt> BigInt::FromNumber(Isolate* isolate,
@ -1472,36 +1494,6 @@ void BigInt::InplaceMultiplyAdd(FreshlyAllocatedBigInt x, uintptr_t factor,
bigint);
}
// Divides {x} by {divisor}, returning the result in {quotient} and {remainder}.
// Mathematically, the contract is:
// quotient = (x - remainder) / divisor, with 0 <= remainder < divisor.
// If {quotient} is an empty handle, an appropriately sized BigInt will be
// allocated for it; otherwise the caller must ensure that it is big enough.
// {quotient} can be the same as {x} for an in-place division. {quotient} can
// also be nullptr if the caller is only interested in the remainder.
void MutableBigInt::AbsoluteDivSmall(Isolate* isolate, Handle<BigIntBase> x,
digit_t divisor,
Handle<MutableBigInt>* quotient,
digit_t* remainder) {
DCHECK_NE(divisor, 0);
DCHECK(!x->is_zero()); // Callers check anyway, no need to handle this.
*remainder = 0;
int length = x->length();
if (quotient != nullptr) {
if ((*quotient).is_null()) {
*quotient = New(isolate, length).ToHandleChecked();
}
for (int i = length - 1; i >= 0; i--) {
digit_t q = digit_div(*remainder, x->digit(i), divisor, remainder);
(*quotient)->set_digit(i, q);
}
} else {
for (int i = length - 1; i >= 0; i--) {
digit_div(*remainder, x->digit(i), divisor, remainder);
}
}
}
MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
Handle<BigIntBase> x,
Handle<BigIntBase> y) {
@ -1782,224 +1774,6 @@ MaybeHandle<BigInt> BigInt::FromSerializedDigits(
return MutableBigInt::MakeImmutable(result);
}
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo(
Isolate* isolate, Handle<BigIntBase> x, int radix,
ShouldThrow should_throw) {
STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits));
DCHECK(base::bits::IsPowerOfTwo(radix));
DCHECK(radix >= 2 && radix <= 32);
DCHECK(!x->is_zero());
const int length = x->length();
const bool sign = x->sign();
const int bits_per_char = base::bits::CountTrailingZeros(radix);
const int char_mask = radix - 1;
// Compute the length of the resulting string: divide the bit length of the
// BigInt by the number of bits representable per character (rounding up).
const digit_t msd = x->digit(length - 1);
const int msd_leading_zeros = base::bits::CountLeadingZeros(msd);
const size_t bit_length = length * kDigitBits - msd_leading_zeros;
const size_t chars_required =
(bit_length + bits_per_char - 1) / bits_per_char + sign;
if (chars_required > String::kMaxLength) {
if (should_throw == kThrowOnError) {
THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String);
} else {
return MaybeHandle<String>();
}
}
Handle<SeqOneByteString> result =
isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked();
DisallowGarbageCollection no_gc;
uint8_t* buffer = result->GetChars(no_gc);
// Print the number into the string, starting from the last position.
int pos = static_cast<int>(chars_required - 1);
digit_t digit = 0;
// Keeps track of how many unprocessed bits there are in {digit}.
int available_bits = 0;
for (int i = 0; i < length - 1; i++) {
digit_t new_digit = x->digit(i);
// Take any leftover bits from the last iteration into account.
int current = (digit | (new_digit << available_bits)) & char_mask;
buffer[pos--] = kConversionChars[current];
int consumed_bits = bits_per_char - available_bits;
digit = new_digit >> consumed_bits;
available_bits = kDigitBits - consumed_bits;
while (available_bits >= bits_per_char) {
buffer[pos--] = kConversionChars[digit & char_mask];
digit >>= bits_per_char;
available_bits -= bits_per_char;
}
}
// Take any leftover bits from the last iteration into account.
int current = (digit | (msd << available_bits)) & char_mask;
buffer[pos--] = kConversionChars[current];
digit = msd >> (bits_per_char - available_bits);
while (digit != 0) {
buffer[pos--] = kConversionChars[digit & char_mask];
digit >>= bits_per_char;
}
if (sign) buffer[pos--] = '-';
DCHECK_EQ(pos, -1);
return result;
}
MaybeHandle<String> MutableBigInt::ToStringGeneric(Isolate* isolate,
Handle<BigIntBase> x,
int radix,
ShouldThrow should_throw) {
DCHECK(radix >= 2 && radix <= 36);
DCHECK(!x->is_zero());
Heap* heap = isolate->heap();
const int length = x->length();
const bool sign = x->sign();
// Compute (an overapproximation of) the length of the resulting string:
// Divide bit length of the BigInt by bits representable per character.
const size_t bit_length =
length * kDigitBits - base::bits::CountLeadingZeros(x->digit(length - 1));
// Maximum number of bits we can represent with one character. We'll use this
// to find an appropriate chunk size below.
const uint8_t max_bits_per_char = kMaxBitsPerChar[radix];
// For estimating result length, we have to be pessimistic and work with
// the minimum number of bits one character can represent.
const uint8_t min_bits_per_char = max_bits_per_char - 1;
// Perform the following computation with uint64_t to avoid overflows.
uint64_t chars_required = bit_length;
chars_required *= kBitsPerCharTableMultiplier;
chars_required += min_bits_per_char - 1; // Round up.
chars_required /= min_bits_per_char;
chars_required += sign;
if (chars_required > String::kMaxLength) {
if (should_throw == kThrowOnError) {
THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String);
} else {
return MaybeHandle<String>();
}
}
Handle<SeqOneByteString> result =
isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked();
#if DEBUG
// Zap the string first.
{
DisallowGarbageCollection no_gc;
uint8_t* chars = result->GetChars(no_gc);
for (int i = 0; i < static_cast<int>(chars_required); i++) chars[i] = '?';
}
#endif
// We assemble the result string in reverse order, and then reverse it.
// TODO(jkummerow): Consider building the string from the right, and
// left-shifting it if the length estimate was too large.
int pos = 0;
digit_t last_digit;
if (length == 1) {
last_digit = x->digit(0);
} else {
int chunk_chars =
kDigitBits * kBitsPerCharTableMultiplier / max_bits_per_char;
digit_t chunk_divisor = digit_pow(radix, chunk_chars);
// By construction of chunk_chars, there can't have been overflow.
DCHECK_NE(chunk_divisor, 0);
int nonzero_digit = length - 1;
DCHECK_NE(x->digit(nonzero_digit), 0);
// {rest} holds the part of the BigInt that we haven't looked at yet.
// Not to be confused with "remainder"!
Handle<MutableBigInt> rest;
// In the first round, divide the input, allocating a new BigInt for
// the result == rest; from then on divide the rest in-place.
Handle<BigIntBase>* dividend = &x;
uintptr_t work_estimate = 0;
do {
digit_t chunk;
AbsoluteDivSmall(isolate, *dividend, chunk_divisor, &rest, &chunk);
DCHECK(!rest.is_null());
dividend = reinterpret_cast<Handle<BigIntBase>*>(&rest);
DisallowGarbageCollection no_gc;
uint8_t* chars = result->GetChars(no_gc);
for (int i = 0; i < chunk_chars; i++) {
chars[pos++] = kConversionChars[chunk % radix];
chunk /= radix;
}
DCHECK_EQ(chunk, 0);
if (rest->digit(nonzero_digit) == 0) nonzero_digit--;
// We can never clear more than one digit per iteration, because
// chunk_divisor is smaller than max digit value.
DCHECK_GT(rest->digit(nonzero_digit), 0);
// String formatting can take a long time. Check for interrupt requests
// every now and then (roughly every 10-20 of milliseconds -- rarely
// enough not to create noticeable overhead, frequently enough not to
// appear frozen).
work_estimate += length;
if (work_estimate > 500000) {
work_estimate = 0;
StackLimitCheck interrupt_check(isolate);
if (interrupt_check.InterruptRequested()) {
{
AllowGarbageCollection might_throw;
if (isolate->stack_guard()->HandleInterrupts().IsException(
isolate)) {
return MaybeHandle<String>();
}
}
// If there was an interrupt request but no termination, reload
// the raw characters pointer (as the string might have moved).
chars = result->GetChars(no_gc);
}
}
} while (nonzero_digit > 0);
last_digit = rest->digit(0);
}
DisallowGarbageCollection no_gc;
uint8_t* chars = result->GetChars(no_gc);
do {
chars[pos++] = kConversionChars[last_digit % radix];
last_digit /= radix;
} while (last_digit > 0);
DCHECK_GE(pos, 1);
DCHECK(pos <= static_cast<int>(chars_required));
// Remove leading zeroes.
while (pos > 1 && chars[pos - 1] == '0') pos--;
if (sign) chars[pos++] = '-';
// Trim any over-allocation (which can happen due to conservative estimates).
if (pos < static_cast<int>(chars_required)) {
result->set_length(pos, kReleaseStore);
int string_size =
SeqOneByteString::SizeFor(static_cast<int>(chars_required));
int needed_size = SeqOneByteString::SizeFor(pos);
if (needed_size < string_size && !heap->IsLargeObject(*result)) {
Address new_end = result->address() + needed_size;
heap->CreateFillerObjectAt(new_end, (string_size - needed_size),
ClearRecordedSlots::kNo);
}
}
// Reverse the string.
for (int i = 0, j = pos - 1; i < j; i++, j--) {
uint8_t tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
#if DEBUG
// Verify that all characters have been written.
DCHECK(result->length() == pos);
for (int i = 0; i < pos; i++) DCHECK_NE(chars[i], '?');
#endif
return result;
}
Handle<BigInt> BigInt::AsIntN(Isolate* isolate, uint64_t n, Handle<BigInt> x) {
if (x->is_zero()) return x;
if (n == 0) return MutableBigInt::Zero(isolate);
@ -2358,91 +2132,6 @@ inline BigInt::digit_t MutableBigInt::digit_mul(digit_t a, digit_t b,
#endif
}
// Returns the quotient.
// quotient = (high << kDigitBits + low - remainder) / divisor
BigInt::digit_t MutableBigInt::digit_div(digit_t high, digit_t low,
digit_t divisor, digit_t* remainder) {
DCHECK(high < divisor);
#if V8_TARGET_ARCH_X64 && (__GNUC__ || __clang__)
digit_t quotient;
digit_t rem;
__asm__("divq %[divisor]"
// Outputs: {quotient} will be in rax, {rem} in rdx.
: "=a"(quotient), "=d"(rem)
// Inputs: put {high} into rdx, {low} into rax, and {divisor} into
// any register or stack slot.
: "d"(high), "a"(low), [divisor] "rm"(divisor));
*remainder = rem;
return quotient;
#elif V8_TARGET_ARCH_IA32 && (__GNUC__ || __clang__)
digit_t quotient;
digit_t rem;
__asm__("divl %[divisor]"
// Outputs: {quotient} will be in eax, {rem} in edx.
: "=a"(quotient), "=d"(rem)
// Inputs: put {high} into edx, {low} into eax, and {divisor} into
// any register or stack slot.
: "d"(high), "a"(low), [divisor] "rm"(divisor));
*remainder = rem;
return quotient;
#else
static const digit_t kHalfDigitBase = 1ull << kHalfDigitBits;
// Adapted from Warren, Hacker's Delight, p. 152.
int s = base::bits::CountLeadingZeros(divisor);
DCHECK_NE(s, kDigitBits); // {divisor} is not 0.
divisor <<= s;
digit_t vn1 = divisor >> kHalfDigitBits;
digit_t vn0 = divisor & kHalfDigitMask;
// {s} can be 0. {low >> kDigitBits} would be undefined behavior, so
// we mask the shift amount with {kShiftMask}, and the result with
// {s_zero_mask} which is 0 if s == 0 and all 1-bits otherwise.
STATIC_ASSERT(sizeof(intptr_t) == sizeof(digit_t));
const int kShiftMask = kDigitBits - 1;
digit_t s_zero_mask =
static_cast<digit_t>(static_cast<intptr_t>(-s) >> (kDigitBits - 1));
digit_t un32 =
(high << s) | ((low >> ((kDigitBits - s) & kShiftMask)) & s_zero_mask);
digit_t un10 = low << s;
digit_t un1 = un10 >> kHalfDigitBits;
digit_t un0 = un10 & kHalfDigitMask;
digit_t q1 = un32 / vn1;
digit_t rhat = un32 - q1 * vn1;
while (q1 >= kHalfDigitBase || q1 * vn0 > rhat * kHalfDigitBase + un1) {
q1--;
rhat += vn1;
if (rhat >= kHalfDigitBase) break;
}
digit_t un21 = un32 * kHalfDigitBase + un1 - q1 * divisor;
digit_t q0 = un21 / vn1;
rhat = un21 - q0 * vn1;
while (q0 >= kHalfDigitBase || q0 * vn0 > rhat * kHalfDigitBase + un0) {
q0--;
rhat += vn1;
if (rhat >= kHalfDigitBase) break;
}
*remainder = (un21 * kHalfDigitBase + un0 - q0 * divisor) >> s;
return q1 * kHalfDigitBase + q0;
#endif
}
// Raises {base} to the power of {exponent}. Does not check for overflow.
BigInt::digit_t MutableBigInt::digit_pow(digit_t base, digit_t exponent) {
digit_t result = 1ull;
while (exponent > 0) {
if (exponent & 1) {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
#undef HAVE_TWODIGIT_T
void MutableBigInt::set_64_bits(uint64_t bits) {