[bigint] Fix corner-case bugs in fast .toString

Bug: v8:11515
Change-Id: Ieece676f2f4ae258db8b7e1783c796ff6c0fa6f4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3055293
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75956}
This commit is contained in:
Jakob Kummerow 2021-07-26 23:26:19 +02:00 committed by V8 LUCI CQ
parent 5404eaf159
commit 2d6ad4deb4
2 changed files with 18 additions and 3 deletions

View File

@ -124,6 +124,7 @@ void ProcessorImpl::InvertNewton(RWDigits Z, Digits V, RWDigits scratch) {
// (3d): U = T * S, truncated so that at least 2k+1 fraction bits remain // (3d): U = T * S, truncated so that at least 2k+1 fraction bits remain
// (U has one integer digit, which might be zero). // (U has one integer digit, which might be zero).
fraction_digits = DIV_CEIL(2 * k + 1, kDigitBits);
RWDigits U(scratch, kUOffset, S.len() + T.len()); RWDigits U(scratch, kUOffset, S.len() + T.len());
DCHECK(U.len() > fraction_digits); DCHECK(U.len() > fraction_digits);
Multiply(U, S, T); Multiply(U, S, T);
@ -202,6 +203,7 @@ void ProcessorImpl::Invert(RWDigits Z, Digits V, RWDigits scratch) {
InvertBasecase(Z, V, scratch); InvertBasecase(Z, V, scratch);
if (Z[vn] == 1) { if (Z[vn] == 1) {
for (int i = 0; i < vn; i++) Z[i] = ~digit_t{0}; for (int i = 0; i < vn; i++) Z[i] = ~digit_t{0};
Z[vn] = 0;
} }
} }
} }

View File

@ -358,7 +358,17 @@ RecursionLevel* RecursionLevel::CreateLevels(digit_t base_divisor,
int target_bit_length, int target_bit_length,
ProcessorImpl* processor) { ProcessorImpl* processor) {
RecursionLevel* level = new RecursionLevel(base_divisor, base_char_count); RecursionLevel* level = new RecursionLevel(base_divisor, base_char_count);
while (BitLength(level->divisor_) * 2 <= target_bit_length) { // We can stop creating levels when the next level's divisor, which is the
// square of the current level's divisor, would be strictly bigger (in terms
// of its numeric value) than the input we're formatting. Since computing that
// next divisor is expensive, we want to predict the necessity based on bit
// lengths. Bit lengths are an imperfect predictor of numeric value, so we
// have to be careful:
// - since we can't estimate which one of two numbers of equal bit length
// is bigger, we have to aim for a strictly bigger bit length.
// - when squaring, the bit length sometimes doubles (e.g. 0b11² == 0b1001),
// but usually we "lose" a bit (e.g. 0b10² == 0b100).
while (BitLength(level->divisor_) * 2 - 1 <= target_bit_length) {
RecursionLevel* prev = level; RecursionLevel* prev = level;
level = new RecursionLevel(prev); level = new RecursionLevel(prev);
processor->Multiply(level->divisor_, prev->divisor_, prev->divisor_); processor->Multiply(level->divisor_, prev->divisor_, prev->divisor_);
@ -439,15 +449,18 @@ char* ToStringFormatter::ProcessLevel(RecursionLevel* level, Digits chunk,
return ProcessLevel(level->next_, chunk, out, is_last_on_level); return ProcessLevel(level->next_, chunk, out, is_last_on_level);
} }
// Step 2: Prepare the chunk. // Step 2: Prepare the chunk.
bool allow_inplace_modification = !level->is_toplevel_; bool allow_inplace_modification = chunk.digits() != digits_.digits();
Digits original_chunk = chunk;
ShiftedDigits chunk_shifted(chunk, level->leading_zero_shift_, ShiftedDigits chunk_shifted(chunk, level->leading_zero_shift_,
allow_inplace_modification); allow_inplace_modification);
chunk = chunk_shifted; chunk = chunk_shifted;
chunk.Normalize(); chunk.Normalize();
// Check (now precisely) if the chunk is smaller than the divisor. // Check (now precisely) if the chunk is smaller than the divisor.
if (Compare(chunk, level->divisor_) <= 0) { if (Compare(chunk, level->divisor_) <= 0) {
// In case we shifted {chunk} in-place, we must undo that before the call. // In case we shifted {chunk} in-place, we must undo that before the call...
chunk_shifted.Reset(); chunk_shifted.Reset();
// ...and otherwise undo the {chunk = chunk_shifted} assignment above.
chunk = original_chunk;
return ProcessLevel(level->next_, chunk, out, is_last_on_level); return ProcessLevel(level->next_, chunk, out, is_last_on_level);
} }
// Step 3: Allocate space for the results. // Step 3: Allocate space for the results.