Reland "[bigint] Karatsuba multiplication"

This is a reland of 59eff3bfaa

Original change's description:
> [bigint] Karatsuba multiplication
>
> The Karatsuba algorithm is used for BigInts with 34 or more internal
> digits, and thanks to better asymptotic complexity provides greater
> speedups the bigger the inputs.
>
> Bug: v8:11515
> Change-Id: I5ab0e318173ea4a02ced3f156d3c17e0259c5036
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2782283
> 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@{#74916}

Bug: v8:11515
Change-Id: I5ece2ff29ef11ea304980c053887d9746cfc80bc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2933497
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74922}
This commit is contained in:
Jakob Kummerow 2021-06-02 15:20:29 +02:00 committed by V8 LUCI CQ
parent 3dd195240b
commit 81dd3f42be
16 changed files with 714 additions and 6 deletions

View File

@ -4898,7 +4898,9 @@ v8_source_set("v8_bigint") {
"src/bigint/bigint-internal.h",
"src/bigint/bigint.h",
"src/bigint/digit-arithmetic.h",
"src/bigint/mul-karatsuba.cc",
"src/bigint/mul-schoolbook.cc",
"src/bigint/util.h",
"src/bigint/vector-arithmetic.cc",
"src/bigint/vector-arithmetic.h",
]

View File

@ -30,7 +30,8 @@ void ProcessorImpl::Multiply(RWDigits Z, Digits X, Digits Y) {
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);
if (Y.len() < kKaratsubaThreshold) return MultiplySchoolbook(Z, X, Y);
return MultiplyKaratsuba(Z, X, Y);
}
Status Processor::Multiply(RWDigits Z, Digits X, Digits Y) {

View File

@ -10,6 +10,8 @@
namespace v8 {
namespace bigint {
constexpr int kKaratsubaThreshold = 34;
class ProcessorImpl : public Processor {
public:
explicit ProcessorImpl(Platform* platform);
@ -21,6 +23,11 @@ class ProcessorImpl : public Processor {
void MultiplySingle(RWDigits Z, Digits X, digit_t y);
void MultiplySchoolbook(RWDigits Z, Digits X, Digits Y);
void MultiplyKaratsuba(RWDigits Z, Digits X, Digits Y);
void KaratsubaStart(RWDigits Z, Digits X, Digits Y, RWDigits scratch, int k);
void KaratsubaChunk(RWDigits Z, Digits X, Digits Y, RWDigits scratch);
void KaratsubaMain(RWDigits Z, Digits X, Digits Y, RWDigits scratch, int n);
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
@ -59,6 +66,28 @@ class ProcessorImpl : public Processor {
#define DCHECK(cond) (void(0))
#endif
// RAII memory for a Digits array.
class Storage {
public:
explicit Storage(int count) : ptr_(new digit_t[count]) {}
digit_t* get() { return ptr_.get(); }
private:
std::unique_ptr<digit_t[]> ptr_;
};
// A writable Digits array with attached storage.
class ScratchDigits : public RWDigits {
public:
explicit ScratchDigits(int len) : RWDigits(nullptr, len), storage_(len) {
digits_ = storage_.get();
}
private:
Storage storage_;
};
} // namespace bigint
} // namespace v8

View File

@ -45,6 +45,36 @@ inline digit_t digit_add3(digit_t a, digit_t b, digit_t c, digit_t* carry) {
#endif
}
// {borrow} will be set to 0 or 1.
inline digit_t digit_sub(digit_t a, digit_t b, digit_t* borrow) {
#if HAVE_TWODIGIT_T
twodigit_t result = twodigit_t{a} - b;
*borrow = (result >> kDigitBits) & 1;
return static_cast<digit_t>(result);
#else
digit_t result = a - b;
*borrow = (result > a) ? 1 : 0;
return result;
#endif
}
// {borrow_out} will be set to 0 or 1.
inline digit_t digit_sub2(digit_t a, digit_t b, digit_t borrow_in,
digit_t* borrow_out) {
#if HAVE_TWODIGIT_T
twodigit_t subtrahend = twodigit_t{b} + borrow_in;
twodigit_t result = twodigit_t{a} - subtrahend;
*borrow_out = (result >> kDigitBits) & 1;
return static_cast<digit_t>(result);
#else
digit_t result = a - b;
*borrow_out = (result > a) ? 1 : 0;
if (result < borrow_in) *borrow_out += 1;
result -= borrow_in;
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

189
src/bigint/mul-karatsuba.cc Normal file
View File

@ -0,0 +1,189 @@
// 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.
// Karatsuba multiplication. This is loosely based on Go's implementation
// found at https://golang.org/src/math/big/nat.go, licensed as follows:
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file [1].
//
// [1] https://golang.org/LICENSE
#include <algorithm>
#include <utility>
#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 {
namespace {
// The Karatsuba algorithm sometimes finishes more quickly when the
// input length is rounded up a bit. This method encodes some heuristics
// to accomplish this. The details have been determined experimentally.
int RoundUpLen(int len) {
if (len <= 36) return RoundUp(len, 2);
// Keep the 4 or 5 most significant non-zero bits.
int shift = BitLength(len) - 5;
if ((len >> shift) >= 0x18) {
shift++;
}
// Round up, unless we're only just above the threshold. This smoothes
// the steps by which time goes up as input size increases.
int additive = ((1 << shift) - 1);
if (shift >= 2 && (len & additive) < (1 << (shift - 2))) {
return len;
}
return ((len + additive) >> shift) << shift;
}
// This method makes the final decision how much to bump up the input size.
int KaratsubaLength(int n) {
n = RoundUpLen(n);
int i = 0;
while (n > kKaratsubaThreshold) {
n >>= 1;
i++;
}
return n << i;
}
// Performs the specific subtraction required by {KaratsubaMain} below.
void KaratsubaSubtractionHelper(RWDigits result, Digits X, Digits Y,
int* sign) {
X.Normalize();
Y.Normalize();
digit_t borrow = 0;
int i = 0;
if (!GreaterThanOrEqual(X, Y)) {
*sign = -(*sign);
std::swap(X, Y);
}
for (; i < Y.len(); i++) {
result[i] = digit_sub2(X[i], Y[i], borrow, &borrow);
}
for (; i < X.len(); i++) {
result[i] = digit_sub(X[i], borrow, &borrow);
}
DCHECK(borrow == 0); // NOLINT(readability/check)
for (; i < result.len(); i++) result[i] = 0;
}
} // namespace
void ProcessorImpl::MultiplyKaratsuba(RWDigits Z, Digits X, Digits Y) {
DCHECK(X.len() >= Y.len());
DCHECK(Y.len() >= kKaratsubaThreshold);
DCHECK(Z.len() >= X.len() + Y.len());
int k = KaratsubaLength(Y.len());
int scratch_len = 4 * k;
ScratchDigits scratch(scratch_len);
KaratsubaStart(Z, X, Y, scratch, k);
}
// Entry point for Karatsuba-based multiplication, takes care of inputs
// with unequal lengths by chopping the larger into chunks.
void ProcessorImpl::KaratsubaStart(RWDigits Z, Digits X, Digits Y,
RWDigits scratch, int k) {
KaratsubaMain(Z, X, Y, scratch, k);
if (should_terminate()) return;
for (int i = 2 * k; i < Z.len(); i++) Z[i] = 0;
if (k < Y.len() || X.len() != Y.len()) {
ScratchDigits T(2 * k);
// Add X0 * Y1 * b.
Digits X0(X, 0, k);
Digits Y1 = Y + std::min(k, Y.len());
if (Y1.len() > 0) {
KaratsubaChunk(T, X0, Y1, scratch);
if (should_terminate()) return;
AddAt(Z + k, T);
}
// Add Xi * Y0 << i and Xi * Y1 * b << (i + k).
Digits Y0(Y, 0, k);
for (int i = k; i < X.len(); i += k) {
Digits Xi(X, i, k);
KaratsubaChunk(T, Xi, Y0, scratch);
if (should_terminate()) return;
AddAt(Z + i, T);
if (Y1.len() > 0) {
KaratsubaChunk(T, Xi, Y1, scratch);
if (should_terminate()) return;
AddAt(Z + (i + k), T);
}
}
}
}
// Entry point for chunk-wise multiplications, selects an appropriate
// algorithm for the inputs based on their sizes.
void ProcessorImpl::KaratsubaChunk(RWDigits Z, Digits X, Digits Y,
RWDigits scratch) {
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]);
if (Y.len() < kKaratsubaThreshold) return MultiplySchoolbook(Z, X, Y);
int k = KaratsubaLength(Y.len());
DCHECK(scratch.len() >= 4 * k);
return KaratsubaStart(Z, X, Y, scratch, k);
}
// The main recursive Karatsuba method.
void ProcessorImpl::KaratsubaMain(RWDigits Z, Digits X, Digits Y,
RWDigits scratch, int n) {
if (n < kKaratsubaThreshold) {
X.Normalize();
Y.Normalize();
if (X.len() >= Y.len()) {
return MultiplySchoolbook(RWDigits(Z, 0, 2 * n), X, Y);
} else {
return MultiplySchoolbook(RWDigits(Z, 0, 2 * n), Y, X);
}
}
DCHECK(scratch.len() >= 4 * n);
DCHECK((n & 1) == 0); // NOLINT(readability/check)
int n2 = n >> 1;
Digits X0(X, 0, n2);
Digits X1(X, n2, n2);
Digits Y0(Y, 0, n2);
Digits Y1(Y, n2, n2);
RWDigits scratch_for_recursion(scratch, 2 * n, 2 * n);
RWDigits P0(scratch, 0, n);
KaratsubaMain(P0, X0, Y0, scratch_for_recursion, n2);
if (should_terminate()) return;
for (int i = 0; i < n; i++) Z[i] = P0[i];
RWDigits P2(scratch, n, n);
KaratsubaMain(P2, X1, Y1, scratch_for_recursion, n2);
if (should_terminate()) return;
RWDigits Z1 = Z + n;
int end = std::min(Z1.len(), P2.len());
for (int i = 0; i < end; i++) Z1[i] = P2[i];
for (int i = end; i < n; i++) {
DCHECK(P2[i] == 0); // NOLINT(readability/check)
}
AddAt(Z + n2, P0);
AddAt(Z + n2, P2);
RWDigits X_diff(scratch, 0, n2);
RWDigits Y_diff(scratch, n2, n2);
int sign = 1;
KaratsubaSubtractionHelper(X_diff, X1, X0, &sign);
KaratsubaSubtractionHelper(Y_diff, Y0, Y1, &sign);
RWDigits P1(scratch, n, n);
KaratsubaMain(P1, X_diff, Y_diff, scratch_for_recursion, n2);
if (sign > 0) {
AddAt(Z + n2, P1);
} else {
SubAt(Z + n2, P1);
}
}
} // namespace bigint
} // namespace v8

View File

@ -71,7 +71,6 @@ void ProcessorImpl::MultiplySchoolbook(RWDigits Z, Digits X, Digits Y) {
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;
@ -85,7 +84,6 @@ void ProcessorImpl::MultiplySchoolbook(RWDigits Z, Digits X, Digits Y) {
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);

60
src/bigint/util.h Normal file
View File

@ -0,0 +1,60 @@
// 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.
// "Generic" helper functions (not specific to BigInts).
#include <stdint.h>
#include <type_traits>
#ifdef _MSC_VER
#include <intrin.h> // For _BitScanReverse.
#endif
#ifndef V8_BIGINT_UTIL_H_
#define V8_BIGINT_UTIL_H_
// Integer division, rounding up.
#define DIV_CEIL(x, y) (((x)-1) / (y) + 1)
namespace v8 {
namespace bigint {
// Rounds up x to a multiple of y.
inline constexpr int RoundUp(int x, int y) { return (x + y - 1) & -y; }
// Different environments disagree on how 64-bit uintptr_t and uint64_t are
// defined, so we have to use templates to be generic.
template <typename T, typename = typename std::enable_if<
std::is_unsigned<T>::value && sizeof(T) == 8>::type>
constexpr int CountLeadingZeros(T value) {
#if __GNUC__ || __clang__
return value == 0 ? 64 : __builtin_clzll(value);
#elif _MSC_VER
unsigned long index = 0; // NOLINT(runtime/int). MSVC insists.
return _BitScanReverse64(&index, value) ? 63 - index : 64;
#else
#error Unsupported compiler.
#endif
}
constexpr int CountLeadingZeros(uint32_t value) {
#if __GNUC__ || __clang__
return value == 0 ? 32 : __builtin_clz(value);
#elif _MSC_VER
unsigned long index = 0; // NOLINT(runtime/int). MSVC insists.
return _BitScanReverse(&index, value) ? 31 - index : 32;
#else
#error Unsupported compiler.
#endif
}
inline constexpr int BitLength(int n) {
return 32 - CountLeadingZeros(static_cast<uint32_t>(n));
}
} // namespace bigint
} // namespace v8
#endif // V8_BIGINT_UTIL_H_

View File

@ -4,9 +4,36 @@
#include "src/bigint/vector-arithmetic.h"
#include "src/bigint/digit-arithmetic.h"
namespace v8 {
namespace bigint {
void AddAt(RWDigits Z, Digits X) {
X.Normalize();
if (X.len() == 0) return;
digit_t carry = 0;
int i = 0;
for (; i < X.len(); i++) {
Z[i] = digit_add3(Z[i], X[i], carry, &carry);
}
for (; carry != 0; i++) {
Z[i] = digit_add2(Z[i], carry, &carry);
}
}
void SubAt(RWDigits Z, Digits X) {
X.Normalize();
digit_t borrow = 0;
int i = 0;
for (; i < X.len(); i++) {
Z[i] = digit_sub2(Z[i], X[i], borrow, &borrow);
}
for (; borrow != 0; i++) {
Z[i] = digit_sub(Z[i], borrow, &borrow);
}
}
int Compare(Digits A, Digits B) {
A.Normalize();
B.Normalize();

View File

@ -12,8 +12,18 @@
namespace v8 {
namespace bigint {
// Z += X.
void AddAt(RWDigits Z, Digits X);
// Z -= X.
void SubAt(RWDigits Z, Digits X);
inline bool IsDigitNormalized(Digits X) { return X.len() == 0 || X.msd() != 0; }
inline bool GreaterThanOrEqual(Digits A, Digits B) {
return Compare(A, B) >= 0;
}
} // namespace bigint
} // namespace v8

View File

@ -19,6 +19,7 @@ group("gn_all") {
]
deps = [
"bigint:bigint_shell",
"inspector:inspector-test",
"mkgrokdump:mkgrokdump",
]

25
test/bigint/BUILD.gn Normal file
View File

@ -0,0 +1,25 @@
# 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.
import("../../gni/v8.gni")
v8_executable("bigint_shell") {
testonly = true
deps = [
"../..:v8_bigint",
"//build/win:default_exe_manifest",
]
data_deps = [ "../../tools:v8_testrunner" ]
data = [
"testcfg.py",
"bigint.status",
]
configs = [ "../..:internal_config_base" ]
sources = [ "bigint-shell.cc" ]
}

3
test/bigint/DEPS Normal file
View File

@ -0,0 +1,3 @@
include_rules = [
"+src/bigint",
]

263
test/bigint/bigint-shell.cc Normal file
View File

@ -0,0 +1,263 @@
// 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/util.h"
namespace v8 {
namespace bigint {
namespace test {
int PrintHelp(char** argv) {
std::cerr << "Usage:\n"
<< argv[0] << " --help\n"
<< " Print this help and exit.\n"
<< argv[0] << " --list\n"
<< " List supported tests.\n"
<< argv[0] << " <testname>\n"
<< " Run the specified test (see --list for a list).\n"
<< "\nOptions when running tests:\n"
<< "--random-seed R\n"
<< " Initialize the random number generator with this seed.\n"
<< "--runs N\n"
<< " Repeat the test N times.\n";
return 1;
}
#define TESTS(V) V(kKaratsuba, "karatsuba")
enum Operation { kNoOp, kList, kTest };
enum Test {
#define TEST(kName, name) kName,
TESTS(TEST)
#undef TEST
};
class RNG {
public:
RNG() = default;
void Initialize(int64_t seed) {
state0_ = MurmurHash3(static_cast<uint64_t>(seed));
state1_ = MurmurHash3(~state0_);
CHECK(state0_ != 0 || state1_ != 0);
}
uint64_t NextUint64() {
XorShift128(&state0_, &state1_);
return static_cast<uint64_t>(state0_ + state1_);
}
static inline void XorShift128(uint64_t* state0, uint64_t* state1) {
uint64_t s1 = *state0;
uint64_t s0 = *state1;
*state0 = s0;
s1 ^= s1 << 23;
s1 ^= s1 >> 17;
s1 ^= s0;
s1 ^= s0 >> 26;
*state1 = s1;
}
static uint64_t MurmurHash3(uint64_t h) {
h ^= h >> 33;
h *= uint64_t{0xFF51AFD7ED558CCD};
h ^= h >> 33;
h *= uint64_t{0xC4CEB9FE1A85EC53};
h ^= h >> 33;
return h;
}
private:
uint64_t state0_;
uint64_t state1_;
};
static constexpr int kCharsPerDigit = kDigitBits / 4;
static const char kConversionChars[] = "0123456789abcdef";
std::string FormatHex(Digits X) {
X.Normalize();
if (X.len() == 0) return "0";
digit_t msd = X.msd();
const int msd_leading_zeros = CountLeadingZeros(msd);
const size_t bit_length = X.len() * kDigitBits - msd_leading_zeros;
const size_t chars = DIV_CEIL(bit_length, 4);
if (chars > 100000) {
return std::string("<BigInt with ") + std::to_string(bit_length) +
std::string(" bits>");
}
std::unique_ptr<char[]> result(new char[chars]);
for (size_t i = 0; i < chars; i++) result[i] = '?';
// Print the number into the string, starting from the last position.
int pos = static_cast<int>(chars - 1);
for (int i = 0; i < X.len() - 1; i++) {
digit_t d = X[i];
for (int j = 0; j < kCharsPerDigit; j++) {
result[pos--] = kConversionChars[d & 15];
d = static_cast<digit_t>(d >> 4u);
}
}
while (msd != 0) {
result[pos--] = kConversionChars[msd & 15];
msd = static_cast<digit_t>(msd >> 4u);
}
CHECK(pos == -1);
return std::string(result.get(), chars);
}
class Runner {
public:
Runner() = default;
void Initialize() {
rng_.Initialize(random_seed_);
processor_.reset(Processor::New(new Platform()));
}
ProcessorImpl* processor() {
return static_cast<ProcessorImpl*>(processor_.get());
}
int Run() {
if (op_ == kList) {
ListTests();
} else if (op_ == kTest) {
RunTest();
} else {
DCHECK(false); // Unreachable.
}
return 0;
}
void ListTests() {
#define PRINT(kName, name) std::cout << name << "\n";
TESTS(PRINT)
#undef PRINT
}
void AssertEquals(Digits input1, Digits input2, Digits expected,
Digits actual) {
if (Compare(expected, actual) == 0) return;
std::cerr << "Input 1: " << FormatHex(input1) << "\n";
std::cerr << "Input 2: " << FormatHex(input2) << "\n";
std::cerr << "Expected: " << FormatHex(expected) << "\n";
std::cerr << "Actual: " << FormatHex(actual) << "\n";
error_ = true;
}
int RunTest() {
int count = 0;
if (test_ == kKaratsuba) {
for (int i = 0; i < runs_; i++) {
TestKaratsuba(&count);
}
} else {
DCHECK(false); // Unreachable.
}
if (error_) return 1;
std::cout << count << " tests run, no error reported.\n";
return 0;
}
void TestKaratsuba(int* count) {
// Calling {MultiplyKaratsuba} directly is only valid if
// left_size >= right_size and right_size >= kKaratsubaThreshold.
for (int right_size = kKaratsubaThreshold;
right_size <= 3 * kKaratsubaThreshold; right_size++) {
for (int left_size = right_size; left_size <= 3 * kKaratsubaThreshold;
left_size++) {
ScratchDigits A(left_size);
ScratchDigits B(right_size);
int result_len = MultiplyResultLength(A, B);
ScratchDigits result(result_len);
ScratchDigits result_schoolbook(result_len);
GenerateRandom(A);
GenerateRandom(B);
processor()->MultiplyKaratsuba(result, A, B);
processor()->MultiplySchoolbook(result_schoolbook, A, B);
AssertEquals(A, B, result_schoolbook, result);
if (error_) return;
(*count)++;
}
}
}
int ParseOptions(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--list") == 0) {
op_ = kList;
} else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
PrintHelp(argv);
return 0;
} else if (strcmp(argv[i], "--random-seed") == 0 ||
strcmp(argv[i], "--random_seed") == 0) {
random_seed_ = std::stoi(argv[++i]);
} else if (strncmp(argv[i], "--random-seed=", 14) == 0 ||
strncmp(argv[i], "--random_seed=", 14) == 0) {
random_seed_ = std::stoi(argv[i] + 14);
} else if (strcmp(argv[i], "--runs") == 0) {
runs_ = std::stoi(argv[++i]);
} else if (strncmp(argv[i], "--runs=", 7) == 0) {
runs_ = std::stoi(argv[i] + 7);
}
#define TEST(kName, name) \
else if (strcmp(argv[i], name) == 0) { \
op_ = kTest; \
test_ = kName; \
}
TESTS(TEST)
#undef TEST
else {
std::cerr << "Warning: ignored argument: " << argv[i] << "\n";
}
}
if (op_ == kNoOp) return PrintHelp(argv); // op is mandatory.
return 0;
}
private:
void GenerateRandom(RWDigits Z) {
if (Z.len() == 0) return;
if (sizeof(digit_t) == 8) {
for (int i = 0; i < Z.len(); i++) {
Z[i] = static_cast<digit_t>(rng_.NextUint64());
}
} else {
for (int i = 0; i < Z.len(); i += 2) {
uint64_t random = rng_.NextUint64();
Z[i] = static_cast<digit_t>(random);
if (i + 1 < Z.len()) Z[i + 1] = static_cast<digit_t>(random >> 32);
}
}
// Special case: we don't want the MSD to be zero.
while (Z.msd() == 0) {
Z[Z.len() - 1] = static_cast<digit_t>(rng_.NextUint64());
}
}
Operation op_{kNoOp};
Test test_;
bool error_{false};
int runs_ = 1;
int64_t random_seed_{314159265359};
RNG rng_;
std::unique_ptr<Processor, Processor::Destroyer> processor_;
};
} // namespace test
} // namespace bigint
} // namespace v8
int main(int argc, char** argv) {
v8::bigint::test::Runner runner;
int ret = runner.ParseOptions(argc, argv);
if (ret != 0) return ret;
runner.Initialize();
return runner.Run();
}

View File

@ -0,0 +1,5 @@
# 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.
[]

63
test/bigint/testcfg.py Normal file
View File

@ -0,0 +1,63 @@
# 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.
# for py2/py3 compatibility
from __future__ import print_function
import os
from testrunner.local import command
from testrunner.local import utils
from testrunner.local import testsuite
from testrunner.objects import testcase
SHELL = 'bigint_shell'
class VariantsGenerator(testsuite.VariantsGenerator):
def _get_variants(self, test):
return self._standard_variant
class TestLoader(testsuite.TestLoader):
def _list_test_filenames(self):
shell = os.path.abspath(
os.path.join(self.test_config.shell_dir, SHELL))
if utils.IsWindows():
shell += ".exe"
cmd = command.Command(
cmd_prefix=self.test_config.command_prefix,
shell=shell,
args=['--list'] + self.test_config.extra_flags)
output = cmd.execute()
if output.exit_code != 0:
print("Test executable failed to list the tests.\n\nCmd:")
print(cmd)
print("\nStdout:")
print(output.stdout)
print("\nStderr:")
print(output.stderr)
print("\nExit code: %d" % output.exit_code)
return sorted(output.stdout.strip().split())
class TestSuite(testsuite.TestSuite):
def _test_loader_class(self):
return TestLoader
def _test_class(self):
return TestCase
class TestCase(testcase.TestCase):
def _get_files_params(self):
return [self.path]
def get_shell(self):
return SHELL
def GetSuite(*args, **kwargs):
return TestSuite(*args, **kwargs)

View File

@ -36,8 +36,8 @@ USE_PTY = "linux" in sys.platform
if USE_PTY:
import pty
BUILD_TARGETS_TEST = ["d8", "cctest", "inspector-test", "unittests",
"wasm_api_tests"]
BUILD_TARGETS_TEST = ["d8", "bigint_shell", "cctest", "inspector-test",
"unittests", "wasm_api_tests"]
BUILD_TARGETS_ALL = ["all"]
# All arches that this script understands.
@ -51,7 +51,8 @@ MODES = ["release", "debug", "optdebug"]
DEFAULT_MODES = ["release", "debug"]
# Build targets that can be manually specified.
TARGETS = ["d8", "cctest", "unittests", "v8_fuzzers", "wasm_api_tests", "wee8",
"mkgrokdump", "generate-bytecode-expectations", "inspector-test"]
"mkgrokdump", "generate-bytecode-expectations", "inspector-test",
"bigint_shell"]
# Build targets that get built when you don't specify any (and specified tests
# don't imply any other targets).
DEFAULT_TARGETS = ["d8"]
@ -81,6 +82,7 @@ HELP = """<arch> can be any of: %(arches)s
"targets": ", ".join(TARGETS)}
TESTSUITES_TARGETS = {"benchmarks": "d8",
"bigint": "bigint_shell",
"cctest": "cctest",
"debugger": "d8",
"fuzzer": "v8_fuzzers",