[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:
parent
ca42a5b86d
commit
bf74af74da
5
BUILD.gn
5
BUILD.gn
@ -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" ]
|
||||
|
@ -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)?'
|
||||
|
43
src/bigint/bigint-internal.cc
Normal file
43
src/bigint/bigint-internal.cc
Normal 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
|
65
src/bigint/bigint-internal.h
Normal file
65
src/bigint/bigint-internal.h
Normal 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_
|
@ -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
|
||||
|
||||
|
87
src/bigint/digit-arithmetic.h
Normal file
87
src/bigint/digit-arithmetic.h
Normal 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_
|
99
src/bigint/mul-schoolbook.cc
Normal file
99
src/bigint/mul-schoolbook.cc
Normal 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
|
@ -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 {
|
||||
|
20
src/bigint/vector-arithmetic.h
Normal file
20
src/bigint/vector-arithmetic.h
Normal 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_
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user