[bigint] Move multiplication to src/bigint/

Also replace the schoolbook algorithm with an optimized version
that runs about twice as fast.
This also adds infrastructure to support interrupt checks from
BigInt library code.

Bug: v8:11515
Change-Id: I5f812913697384afca98937e1fb7361b4ec22d62
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2773043
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74045}
This commit is contained in:
Jakob Kummerow 2021-04-19 16:48:37 +02:00 committed by Commit Bot
parent ca42a5b86d
commit bf74af74da
13 changed files with 488 additions and 74 deletions

View File

@ -4826,8 +4826,13 @@ v8_source_set("fuzzer_support") {
v8_source_set("v8_bigint") {
sources = [
"src/bigint/bigint-internal.cc",
"src/bigint/bigint-internal.h",
"src/bigint/bigint.h",
"src/bigint/digit-arithmetic.h",
"src/bigint/mul-schoolbook.cc",
"src/bigint/vector-arithmetic.cc",
"src/bigint/vector-arithmetic.h",
]
configs = [ ":internal_config" ]

View File

@ -279,7 +279,7 @@ def _CheckHeadersHaveIncludeGuards(input_api, output_api):
for line in f.NewContents():
for i in range(len(guard_patterns)):
if guard_patterns[i].match(line):
found_patterns[i] = True
found_patterns[i] = True
if skip_check_pattern.match(line):
file_omitted = True
break
@ -485,7 +485,9 @@ def _CheckNoexceptAnnotations(input_api, output_api):
files_to_check=(r'src[\\\/].*', r'test[\\\/].*'),
# Skip api.cc since we cannot easily add the 'noexcept' annotation to
# public methods.
files_to_skip=(r'src[\\\/]api[\\\/]api\.cc',))
# Skip src/bigint/ because it's meant to be V8-independent.
files_to_skip=(r'src[\\\/]api[\\\/]api\.cc',
r'src[\\\/]bigint[\\\/].*'))
# matches any class name.
class_name = r'\b([A-Z][A-Za-z0-9_:]*)(?:::\1)?'

View File

@ -0,0 +1,43 @@
// 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"
namespace v8 {
namespace bigint {
ProcessorImpl::ProcessorImpl(Platform* platform) : platform_(platform) {}
ProcessorImpl::~ProcessorImpl() { delete platform_; }
Status ProcessorImpl::get_and_clear_status() {
Status result = status_;
status_ = Status::kOk;
return result;
}
Processor* Processor::New(Platform* platform) {
ProcessorImpl* impl = new ProcessorImpl(platform);
return static_cast<Processor*>(impl);
}
void Processor::Destroy() { delete static_cast<ProcessorImpl*>(this); }
void ProcessorImpl::Multiply(RWDigits Z, Digits X, Digits Y) {
X.Normalize();
Y.Normalize();
if (X.len() == 0 || Y.len() == 0) return Z.Clear();
if (X.len() < Y.len()) std::swap(X, Y);
if (Y.len() == 1) return MultiplySingle(Z, X, Y[0]);
return MultiplySchoolbook(Z, X, Y);
}
Status Processor::Multiply(RWDigits Z, Digits X, Digits Y) {
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
impl->Multiply(Z, X, Y);
return impl->get_and_clear_status();
}
} // namespace bigint
} // namespace v8

View File

@ -0,0 +1,65 @@
// 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.
#ifndef V8_BIGINT_BIGINT_INTERNAL_H_
#define V8_BIGINT_BIGINT_INTERNAL_H_
#include "src/bigint/bigint.h"
namespace v8 {
namespace bigint {
class ProcessorImpl : public Processor {
public:
explicit ProcessorImpl(Platform* platform);
~ProcessorImpl();
Status get_and_clear_status();
void Multiply(RWDigits Z, Digits X, Digits Y);
void MultiplySingle(RWDigits Z, Digits X, digit_t y);
void MultiplySchoolbook(RWDigits Z, Digits X, Digits Y);
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
// enough not to appear stuck, rarely enough not to cause noticeable
// overhead).
static const uintptr_t kWorkEstimateThreshold = 5000000;
void AddWorkEstimate(uintptr_t estimate) {
work_estimate_ += estimate;
if (work_estimate_ >= kWorkEstimateThreshold) {
work_estimate_ = 0;
if (platform_->InterruptRequested()) {
status_ = Status::kInterrupted;
}
}
}
bool should_terminate() { return status_ == Status::kInterrupted; }
uintptr_t work_estimate_{0};
Status status_{Status::kOk};
Platform* platform_;
};
#define CHECK(cond) \
if (!(cond)) { \
std::cerr << __FILE__ << ":" << __LINE__ << ": "; \
std::cerr << "Assertion failed: " #cond "\n"; \
abort(); \
}
#ifdef DEBUG
#define DCHECK(cond) CHECK(cond)
#else
#define DCHECK(cond) (void(0))
#endif
} // namespace bigint
} // namespace v8
#endif // V8_BIGINT_BIGINT_INTERNAL_H_

View File

@ -120,9 +120,117 @@ class Digits {
}
};
// Writable version of a Digits array.
// Does not own the memory it points at.
class RWDigits : public Digits {
public:
RWDigits(digit_t* mem, int len) : Digits(mem, len) {}
RWDigits(RWDigits src, int offset, int len) : Digits(src, offset, len) {}
RWDigits operator+(int i) {
BIGINT_H_DCHECK(i >= 0 && i <= len_);
return RWDigits(digits_ + i, len_ - i);
}
#if UINTPTR_MAX == 0xFFFFFFFF
digit_t& operator[](int i) {
BIGINT_H_DCHECK(i >= 0 && i < len_);
return digits_[i];
}
#else
// 64-bit platform. We only require digits arrays to be 4-byte aligned,
// so we use a wrapper class to allow regular array syntax while
// performing unaligned memory accesses under the hood.
class WritableDigitReference {
public:
// Support "X[i] = x" notation.
void operator=(digit_t digit) { memcpy(ptr_, &digit, sizeof(digit)); }
// Support "X[i] = Y[j]" notation.
WritableDigitReference& operator=(const WritableDigitReference& src) {
memcpy(ptr_, src.ptr_, sizeof(digit_t));
return *this;
}
// Support "x = X[i]" notation.
operator digit_t() {
digit_t result;
memcpy(&result, ptr_, sizeof(result));
return result;
}
private:
// This class is not for public consumption.
friend class RWDigits;
// Primary constructor.
explicit WritableDigitReference(digit_t* ptr)
: ptr_(reinterpret_cast<uint32_t*>(ptr)) {}
// Required for returning WDR instances from "operator[]" below.
WritableDigitReference(const WritableDigitReference& src) = default;
uint32_t* ptr_;
};
WritableDigitReference operator[](int i) {
BIGINT_H_DCHECK(i >= 0 && i < len_);
return WritableDigitReference(digits_ + i);
}
#endif
digit_t* digits() { return digits_; }
void set_len(int len) { len_ = len; }
void Clear() { memset(digits_, 0, len_ * sizeof(digit_t)); }
};
class Platform {
public:
virtual ~Platform() = default;
// If you want the ability to interrupt long-running operations, implement
// a Platform subclass that overrides this method. It will be queried
// every now and then by long-running operations.
virtual bool InterruptRequested() { return false; }
};
// These are the operations that this library supports.
// The signatures follow the convention:
//
// void Operation(RWDigits results, Digits inputs);
//
// You must preallocate the result; use the respective {OperationResultLength}
// function to determine its minimum required length. The actual result may
// be smaller, so you should call result.Normalize() on the result.
//
// The operations are divided into two groups: "fast" (O(n) with small
// coefficient) operations are exposed directly as free functions, "slow"
// operations are methods on a {BigIntProcessor} object, which provides
// support for interrupting execution via the {Platform}'s {InterruptRequested}
// mechanism when it takes too long. These functions return a {Status} value.
// Returns r such that r < 0 if A < B; r > 0 if A > B; r == 0 if A == B.
int Compare(Digits A, Digits B);
enum class Status { kOk, kInterrupted };
class Processor {
public:
// Takes ownership of {platform}.
static Processor* New(Platform* platform);
// Use this for any std::unique_ptr holding an instance of BigIntProcessor.
class Destroyer {
public:
void operator()(Processor* proc) { proc->Destroy(); }
};
// When not using std::unique_ptr, call this to delete the instance.
void Destroy();
// Z := X * Y
Status Multiply(RWDigits Z, Digits X, Digits Y);
};
inline int MultiplyResultLength(Digits X, Digits Y) {
return X.len() + Y.len();
}
} // namespace bigint
} // namespace v8

View File

@ -0,0 +1,87 @@
// 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.
// Helper functions that operate on individual digits.
#ifndef V8_BIGINT_DIGIT_ARITHMETIC_H_
#define V8_BIGINT_DIGIT_ARITHMETIC_H_
#include "src/bigint/bigint.h"
namespace v8 {
namespace bigint {
static constexpr int kHalfDigitBits = kDigitBits / 2;
static constexpr digit_t kHalfDigitBase = digit_t{1} << kHalfDigitBits;
static constexpr digit_t kHalfDigitMask = kHalfDigitBase - 1;
// {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
twodigit_t result = twodigit_t{a} + b;
*carry = result >> kDigitBits;
return static_cast<digit_t>(result);
#else
digit_t result = a + b;
*carry = (result < a) ? 1 : 0;
return result;
#endif
}
// This compiles to slightly better machine code than repeated invocations
// of {digit_add2}.
inline digit_t digit_add3(digit_t a, digit_t b, digit_t c, digit_t* carry) {
#if HAVE_TWODIGIT_T
twodigit_t result = twodigit_t{a} + b + c;
*carry = result >> kDigitBits;
return static_cast<digit_t>(result);
#else
digit_t result = a + b;
*carry = (result < a) ? 1 : 0;
result += c;
if (result < c) *carry += 1;
return result;
#endif
}
// Returns the low half of the result. High half is in {high}.
inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high) {
#if HAVE_TWODIGIT_T
twodigit_t result = twodigit_t{a} * b;
*high = result >> kDigitBits;
return static_cast<digit_t>(result);
#else
// Multiply in half-pointer-sized chunks.
// For inputs [AH AL]*[BH BL], the result is:
//
// [AL*BL] // r_low
// + [AL*BH] // r_mid1
// + [AH*BL] // r_mid2
// + [AH*BH] // r_high
// = [R4 R3 R2 R1] // high = [R4 R3], low = [R2 R1]
//
// Where of course we must be careful with carries between the columns.
digit_t a_low = a & kHalfDigitMask;
digit_t a_high = a >> kHalfDigitBits;
digit_t b_low = b & kHalfDigitMask;
digit_t b_high = b >> kHalfDigitBits;
digit_t r_low = a_low * b_low;
digit_t r_mid1 = a_low * b_high;
digit_t r_mid2 = a_high * b_low;
digit_t r_high = a_high * b_high;
digit_t carry = 0;
digit_t low = digit_add3(r_low, r_mid1 << kHalfDigitBits,
r_mid2 << kHalfDigitBits, &carry);
*high =
(r_mid1 >> kHalfDigitBits) + (r_mid2 >> kHalfDigitBits) + r_high + carry;
return low;
#endif
}
} // namespace bigint
} // namespace v8
#endif // V8_BIGINT_DIGIT_ARITHMETIC_H_

View File

@ -0,0 +1,99 @@
// 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 {
// Z := X * y, where y is a single digit.
void ProcessorImpl::MultiplySingle(RWDigits Z, Digits X, digit_t y) {
DCHECK(y != 0); // NOLINT(readability/check)
digit_t carry = 0;
digit_t high = 0;
for (int i = 0; i < X.len(); i++) {
digit_t new_high;
digit_t low = digit_mul(X[i], y, &new_high);
Z[i] = digit_add3(low, high, carry, &carry);
high = new_high;
}
AddWorkEstimate(X.len());
Z[X.len()] = carry + high;
for (int i = X.len() + 1; i < Z.len(); i++) Z[i] = 0;
}
#define BODY(min, max) \
for (int j = min; j <= max; j++) { \
digit_t high; \
digit_t low = digit_mul(X[j], Y[i - j], &high); \
digit_t carrybit; \
zi = digit_add2(zi, low, &carrybit); \
carry += carrybit; \
next = digit_add2(next, high, &carrybit); \
next_carry += carrybit; \
} \
Z[i] = zi
// Z := X * Y.
// O(n²) "schoolbook" multiplication algorithm. Optimized to minimize
// bounds and overflow checks: rather than looping over X for every digit
// of Y (or vice versa), we loop over Z. The {BODY} macro above is what
// computes one of Z's digits as a sum of the products of relevant digits
// of X and Y. This yields a nearly 2x improvement compared to more obvious
// implementations.
// This method is *highly* performance sensitive even for the advanced
// algorithms, which use this as the base case of their recursive calls.
void ProcessorImpl::MultiplySchoolbook(RWDigits Z, Digits X, Digits Y) {
DCHECK(IsDigitNormalized(X));
DCHECK(IsDigitNormalized(Y));
DCHECK(X.len() >= Y.len());
DCHECK(Z.len() >= X.len() + Y.len());
if (X.len() == 0 || Y.len() == 0) return Z.Clear();
digit_t next, next_carry = 0, carry = 0;
// Unrolled first iteration: it's trivial.
Z[0] = digit_mul(X[0], Y[0], &next);
int i = 1;
// Unrolled second iteration: a little less setup.
if (i < Y.len()) {
digit_t zi = next;
next = 0;
BODY(0, 1);
i++;
}
// Main part: since X.len() >= Y.len() > i, no bounds checks are needed.
for (; i < Y.len(); i++) {
digit_t zi = digit_add2(next, carry, &carry);
next = next_carry + carry;
carry = 0;
next_carry = 0;
BODY(0, i);
AddWorkEstimate(i);
if (should_terminate()) return;
}
// Last part: i exceeds Y now, we have to be careful about bounds.
int loop_end = X.len() + Y.len() - 2;
for (; i <= loop_end; i++) {
int max_x_index = std::min(i, X.len() - 1);
int max_y_index = Y.len() - 1;
int min_x_index = i - max_y_index;
digit_t zi = digit_add2(next, carry, &carry);
next = next_carry + carry;
carry = 0;
next_carry = 0;
BODY(min_x_index, max_x_index);
AddWorkEstimate(max_x_index - min_x_index);
if (should_terminate()) return;
}
// Write the last digit, and zero out any extra space in Z.
Z[i++] = digit_add2(next, carry, &carry);
DCHECK(carry == 0); // NOLINT(readability/check)
for (; i < Z.len(); i++) Z[i] = 0;
}
#undef BODY
} // namespace bigint
} // namespace v8

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/bigint/bigint.h"
#include "src/bigint/vector-arithmetic.h"
namespace v8 {
namespace bigint {

View File

@ -0,0 +1,20 @@
// 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.
// Helper functions that operate on {Digits} vectors of digits.
#ifndef V8_BIGINT_VECTOR_ARITHMETIC_H_
#define V8_BIGINT_VECTOR_ARITHMETIC_H_
#include "src/bigint/bigint.h"
namespace v8 {
namespace bigint {
inline bool IsDigitNormalized(Digits X) { return X.len() == 0 || X.msd() != 0; }
} // namespace bigint
} // namespace v8
#endif // V8_BIGINT_VECTOR_ARITHMETIC_H_

View File

@ -22,6 +22,7 @@
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/base/utils/random-number-generator.h"
#include "src/bigint/bigint.h"
#include "src/builtins/builtins-promise.h"
#include "src/builtins/constants-table-builder.h"
#include "src/codegen/assembler-inl.h"
@ -3212,6 +3213,8 @@ Isolate::~Isolate() {
delete thread_manager_;
thread_manager_ = nullptr;
bigint_processor_->Destroy();
delete global_handles_;
global_handles_ = nullptr;
delete eternal_handles_;
@ -3504,6 +3507,21 @@ using MapOfLoadsAndStoresPerFunction =
std::map<std::string /* function_name */,
std::pair<uint64_t /* loads */, uint64_t /* stores */>>;
MapOfLoadsAndStoresPerFunction* stack_access_count_map = nullptr;
class BigIntPlatform : public bigint::Platform {
public:
explicit BigIntPlatform(Isolate* isolate) : isolate_(isolate) {}
~BigIntPlatform() override = default;
bool InterruptRequested() override {
StackLimitCheck interrupt_check(isolate_);
return (interrupt_check.InterruptRequested() &&
isolate_->stack_guard()->HasTerminationRequest());
}
private:
Isolate* isolate_;
};
} // namespace
bool Isolate::Init(SnapshotData* startup_snapshot_data,
@ -3552,6 +3570,7 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
heap_profiler_ = new HeapProfiler(heap());
interpreter_ = new interpreter::Interpreter(this);
string_table_.reset(new StringTable(this));
bigint_processor_ = bigint::Processor::New(new BigIntPlatform(this));
compiler_dispatcher_ =
new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);

View File

@ -54,6 +54,10 @@ namespace base {
class RandomNumberGenerator;
} // namespace base
namespace bigint {
class Processor;
}
namespace debug {
class ConsoleDelegate;
class AsyncEventDelegate;
@ -1138,6 +1142,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
ThreadManager* thread_manager() const { return thread_manager_; }
bigint::Processor* bigint_processor() { return bigint_processor_; }
#ifndef V8_INTL_SUPPORT
unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize() {
return &jsregexp_uncanonicalize_;
@ -1877,6 +1883,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
GlobalHandles* global_handles_ = nullptr;
EternalHandles* eternal_handles_ = nullptr;
ThreadManager* thread_manager_ = nullptr;
bigint::Processor* bigint_processor_ = nullptr;
RuntimeState runtime_state_;
Builtins builtins_;
SetupIsolateDelegate* setup_delegate_ = nullptr;

View File

@ -136,10 +136,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
MutableBigInt result_storage = MutableBigInt());
static void MultiplyAccumulate(Handle<BigIntBase> multiplicand,
digit_t multiplier,
Handle<MutableBigInt> accumulator,
int accumulator_index);
static void InternalMultiplyAdd(BigIntBase source, digit_t factor,
digit_t summand, int n, MutableBigInt result);
void InplaceMultiplyAdd(uintptr_t factor, uintptr_t summand);
@ -243,14 +239,25 @@ NEVER_READ_ONLY_SPACE_IMPL(MutableBigInt)
#include "src/base/platform/wrappers.h"
#include "src/objects/object-macros-undef.h"
struct GetDigits : bigint::Digits {
explicit GetDigits(Handle<BigIntBase> bigint) : GetDigits(*bigint) {}
explicit GetDigits(BigIntBase bigint)
: bigint::Digits(
reinterpret_cast<bigint::digit_t*>(
bigint.ptr() + BigIntBase::kDigitsOffset - kHeapObjectTag),
bigint.length()) {}
};
bigint::Digits GetDigits(BigIntBase bigint) {
return bigint::Digits(
reinterpret_cast<bigint::digit_t*>(
bigint.ptr() + BigIntBase::kDigitsOffset - kHeapObjectTag),
bigint.length());
}
bigint::Digits GetDigits(Handle<BigIntBase> bigint) {
return GetDigits(*bigint);
}
bigint::RWDigits GetRWDigits(MutableBigInt bigint) {
return bigint::RWDigits(
reinterpret_cast<bigint::digit_t*>(
bigint.ptr() + BigIntBase::kDigitsOffset - kHeapObjectTag),
bigint.length());
}
bigint::RWDigits GetRWDigits(Handle<MutableBigInt> bigint) {
return GetRWDigits(*bigint);
}
template <typename T, typename Isolate>
MaybeHandle<T> ThrowBigIntTooBig(Isolate* isolate) {
@ -531,29 +538,18 @@ MaybeHandle<BigInt> BigInt::Multiply(Isolate* isolate, Handle<BigInt> x,
Handle<BigInt> y) {
if (x->is_zero()) return x;
if (y->is_zero()) return y;
int result_length = x->length() + y->length();
int result_length = bigint::MultiplyResultLength(GetDigits(x), GetDigits(y));
Handle<MutableBigInt> result;
if (!MutableBigInt::New(isolate, result_length).ToHandle(&result)) {
return MaybeHandle<BigInt>();
}
result->InitializeDigits(result_length);
uintptr_t work_estimate = 0;
for (int i = 0; i < x->length(); i++) {
MutableBigInt::MultiplyAccumulate(y, x->digit(i), result, i);
// Multiplication 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 += y->length();
if (work_estimate > 5000000) {
work_estimate = 0;
StackLimitCheck interrupt_check(isolate);
if (interrupt_check.InterruptRequested() &&
isolate->stack_guard()->HandleInterrupts().IsException(isolate)) {
return MaybeHandle<BigInt>();
}
}
DisallowGarbageCollection no_gc;
bigint::Status status = isolate->bigint_processor()->Multiply(
GetRWDigits(result), GetDigits(x), GetDigits(y));
if (status == bigint::Status::kInterrupted) {
AllowGarbageCollection terminating_anyway;
isolate->TerminateExecution();
return {};
}
result->set_sign(x->sign() != y->sign());
return MutableBigInt::MakeImmutable(result);
@ -1449,46 +1445,6 @@ Handle<MutableBigInt> MutableBigInt::AbsoluteXor(Isolate* isolate,
[](digit_t a, digit_t b) { return a ^ b; });
}
// Multiplies {multiplicand} with {multiplier} and adds the result to
// {accumulator}, starting at {accumulator_index} for the least-significant
// digit.
// Callers must ensure that {accumulator} is big enough to hold the result.
void MutableBigInt::MultiplyAccumulate(Handle<BigIntBase> multiplicand,
digit_t multiplier,
Handle<MutableBigInt> accumulator,
int accumulator_index) {
// This is a minimum requirement; the DCHECK in the second loop below
// will enforce more as needed.
DCHECK(accumulator->length() > multiplicand->length() + accumulator_index);
if (multiplier == 0L) return;
digit_t carry = 0;
digit_t high = 0;
for (int i = 0; i < multiplicand->length(); i++, accumulator_index++) {
digit_t acc = accumulator->digit(accumulator_index);
digit_t new_carry = 0;
// Add last round's carryovers.
acc = digit_add(acc, high, &new_carry);
acc = digit_add(acc, carry, &new_carry);
// Compute this round's multiplication.
digit_t m_digit = multiplicand->digit(i);
digit_t low = digit_mul(multiplier, m_digit, &high);
acc = digit_add(acc, low, &new_carry);
// Store result and prepare for next round.
accumulator->set_digit(accumulator_index, acc);
carry = new_carry;
}
for (; carry != 0 || high != 0; accumulator_index++) {
DCHECK(accumulator_index < accumulator->length());
digit_t acc = accumulator->digit(accumulator_index);
digit_t new_carry = 0;
acc = digit_add(acc, high, &new_carry);
high = 0;
acc = digit_add(acc, carry, &new_carry);
accumulator->set_digit(accumulator_index, acc);
carry = new_carry;
}
}
// Multiplies {source} with {factor} and adds {summand} to the result.
// {result} and {source} may be the same BigInt for inplace modification.
void MutableBigInt::InternalMultiplyAdd(BigIntBase source, digit_t factor,

View File

@ -63,6 +63,8 @@ from testrunner.local import utils
# runtime/references: As of May 2020 the C++ style guide suggests using
# references for out parameters, see
# https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs.
# whitespace/braces: Doesn't handle {}-initialization for custom types
# well; also should be subsumed by clang-format.
LINT_RULES = """
-build/header_guard
@ -70,6 +72,7 @@ LINT_RULES = """
-readability/fn_size
-readability/multiline_comment
-runtime/references
-whitespace/braces
-whitespace/comments
""".split()