[bigint] Implement Add, Subtract, UnaryMinus

Bug: v8:6791
Change-Id: I058db23c03451dc5028c3d39af8607d31048295c
Reviewed-on: https://chromium-review.googlesource.com/667809
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48075}
This commit is contained in:
Jakob Kummerow 2017-09-18 20:32:57 -07:00 committed by Commit Bot
parent 1bc42ab44c
commit 52531a6b96
2 changed files with 208 additions and 6 deletions

View File

@ -2,6 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Parts of the implementation below:
// Copyright (c) 2014 the Dart project authors. Please see the AUTHORS file [1]
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file [2].
//
// [1] https://github.com/dart-lang/sdk/blob/master/AUTHORS
// [2] https://github.com/dart-lang/sdk/blob/master/LICENSE
// 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 [3].
//
// [3] https://golang.org/LICENSE
#include "src/objects/bigint.h"
#include "src/objects-inl.h"
@ -10,7 +25,13 @@ namespace v8 {
namespace internal {
Handle<BigInt> BigInt::UnaryMinus(Handle<BigInt> x) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
// Special case: There is no -0n.
if (x->is_zero()) {
return x;
}
Handle<BigInt> result = BigInt::Copy(x);
result->set_sign(!x->sign());
return result;
}
Handle<BigInt> BigInt::BitwiseNot(Handle<BigInt> x) {
@ -35,11 +56,33 @@ MaybeHandle<BigInt> BigInt::Remainder(Handle<BigInt> x, Handle<BigInt> y) {
}
Handle<BigInt> BigInt::Add(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
bool xsign = x->sign();
if (xsign == y->sign()) {
// x + y == x + y
// -x + -y == -(x + y)
return AbsoluteAdd(x, y, xsign);
}
// x + -y == x - y == -(y - x)
// -x + y == y - x == -(x - y)
if (AbsoluteCompare(x, y) >= 0) {
return AbsoluteSub(x, y, xsign);
}
return AbsoluteSub(y, x, !xsign);
}
Handle<BigInt> BigInt::Subtract(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
bool xsign = x->sign();
if (xsign != y->sign()) {
// x - (-y) == x + y
// (-x) - y == -(x + y)
return AbsoluteAdd(x, y, xsign);
}
// x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y)
if (AbsoluteCompare(x, y) >= 0) {
return AbsoluteSub(x, y, xsign);
}
return AbsoluteSub(y, x, !xsign);
}
Handle<BigInt> BigInt::LeftShift(Handle<BigInt> x, Handle<BigInt> y) {
@ -83,7 +126,7 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
Handle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
// TODO(jkummerow): Support non-power-of-two radixes.
if (!base::bits::IsPowerOfTwo(radix)) radix = 16;
return bigint->ToStringBasePowerOfTwo(bigint, radix);
return ToStringBasePowerOfTwo(bigint, radix);
}
void BigInt::Initialize(int length, bool zero_initialize) {
@ -104,6 +147,105 @@ void BigInt::Initialize(int length, bool zero_initialize) {
// Private helpers for public methods.
Handle<BigInt> BigInt::AbsoluteAdd(Handle<BigInt> x, Handle<BigInt> y,
bool result_sign) {
if (x->length() < y->length()) return AbsoluteAdd(y, x, result_sign);
if (x->is_zero()) {
DCHECK(y->is_zero());
return x;
}
if (y->is_zero()) {
return result_sign == x->sign() ? x : UnaryMinus(x);
}
Handle<BigInt> result =
x->GetIsolate()->factory()->NewBigIntRaw(x->length() + 1);
digit_t carry = 0;
int i = 0;
for (; i < y->length(); i++) {
digit_t new_carry = 0;
digit_t sum = digit_add(x->digit(i), y->digit(i), &new_carry);
sum = digit_add(sum, carry, &new_carry);
result->set_digit(i, sum);
carry = new_carry;
}
for (; i < x->length(); i++) {
digit_t new_carry = 0;
digit_t sum = digit_add(x->digit(i), carry, &new_carry);
result->set_digit(i, sum);
carry = new_carry;
}
result->set_digit(i, carry);
result->set_sign(result_sign);
result->RightTrim();
return result;
}
Handle<BigInt> BigInt::AbsoluteSub(Handle<BigInt> x, Handle<BigInt> y,
bool result_sign) {
DCHECK(x->length() >= y->length());
SLOW_DCHECK(AbsoluteCompare(x, y) >= 0);
if (x->is_zero()) {
DCHECK(y->is_zero());
return x;
}
if (y->is_zero()) {
return result_sign == x->sign() ? x : UnaryMinus(x);
}
Handle<BigInt> result = x->GetIsolate()->factory()->NewBigIntRaw(x->length());
digit_t borrow = 0;
int i = 0;
for (; i < y->length(); i++) {
digit_t new_borrow = 0;
digit_t difference = digit_sub(x->digit(i), y->digit(i), &new_borrow);
difference = digit_sub(difference, borrow, &new_borrow);
result->set_digit(i, difference);
borrow = new_borrow;
}
for (; i < x->length(); i++) {
digit_t new_borrow = 0;
digit_t difference = digit_sub(x->digit(i), borrow, &new_borrow);
result->set_digit(i, difference);
borrow = new_borrow;
}
DCHECK_EQ(0, borrow);
result->set_sign(result_sign);
result->RightTrim();
return result;
}
int BigInt::AbsoluteCompare(Handle<BigInt> x, Handle<BigInt> y) {
int diff = x->length() - y->length();
if (diff != 0) return diff;
int i = x->length() - 1;
while (i >= 0 && x->digit(i) == y->digit(i)) i--;
if (i < 0) return 0;
return x->digit(i) > y->digit(i) ? 1 : -1;
}
Handle<BigInt> BigInt::Copy(Handle<BigInt> source) {
int length = source->length();
Handle<BigInt> result = source->GetIsolate()->factory()->NewBigIntRaw(length);
memcpy(result->address() + HeapObject::kHeaderSize,
source->address() + HeapObject::kHeaderSize,
SizeFor(length) - HeapObject::kHeaderSize);
return result;
}
void BigInt::RightTrim() {
int old_length = length();
int new_length = old_length;
while (digit(new_length - 1) == 0) new_length--;
int to_trim = old_length - new_length;
if (to_trim == 0) return;
int size_delta = to_trim * kDigitSize;
Address new_end = this->address() + SizeFor(new_length);
Heap* heap = GetHeap();
heap->CreateFillerObjectAt(new_end, size_delta, ClearRecordedSlots::kNo);
// Canonicalize -0n.
if (new_length == 0) set_sign(false);
set_length(new_length);
}
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// TODO(jkummerow): Add more tests for this when it is exposed on
@ -114,7 +256,7 @@ Handle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, int radix) {
DCHECK(radix >= 2 && radix <= 32);
Factory* factory = x->GetIsolate()->factory();
// TODO(jkummerow): check in caller?
if (is_zero()) return factory->NewStringFromStaticChars("0");
if (x->is_zero()) return factory->NewStringFromStaticChars("0");
const int len = x->length();
const bool sign = x->sign();
@ -128,6 +270,9 @@ Handle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, int radix) {
chars_for_msd++;
}
// All other digits need chars_per_digit characters; a leading "-" needs one.
if ((String::kMaxLength - chars_for_msd - sign) / chars_per_digit < len - 1) {
CHECK(false); // TODO(jkummerow): Throw instead of crashing.
}
const int chars = chars_for_msd + (len - 1) * chars_per_digit + sign;
Handle<SeqOneByteString> result =
@ -151,6 +296,48 @@ Handle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, int radix) {
return result;
}
// Digit arithmetic helpers.
#if V8_TARGET_ARCH_32_BIT
#define HAVE_TWODIGIT_T 1
typedef uint64_t twodigit_t;
#elif defined(__SIZEOF_INT128__)
// Both Clang and GCC support this on x64.
#define HAVE_TWODIGIT_T 1
typedef __uint128_t twodigit_t;
#endif
// {carry} must point to an initialized digit_t and will either be incremented
// by one or left alone.
inline BigInt::digit_t BigInt::digit_add(digit_t a, digit_t b, digit_t* carry) {
#if HAVE_TWODIGIT_T
twodigit_t result = static_cast<twodigit_t>(a) + static_cast<twodigit_t>(b);
*carry += result >> kDigitBits;
return static_cast<digit_t>(result);
#else
digit_t result = a + b;
if (result < a) *carry += 1;
return result;
#endif
}
// {borrow} must point to an initialized digit_t and will either be incremented
// by one or left alone.
inline BigInt::digit_t BigInt::digit_sub(digit_t a, digit_t b,
digit_t* borrow) {
#if HAVE_TWODIGIT_T
twodigit_t result = static_cast<twodigit_t>(a) - static_cast<twodigit_t>(b);
*borrow += (result >> kDigitBits) & 1;
return static_cast<digit_t>(result);
#else
digit_t result = a - b;
if (result > a) *borrow += 1;
return static_cast<digit_t>(result);
#endif
}
#undef HAVE_TWODIGIT_T
#ifdef OBJECT_PRINT
void BigInt::BigIntPrint(std::ostream& os) {
DisallowHeapAllocation no_gc;

View File

@ -89,7 +89,22 @@ class BigInt : public HeapObject {
static const digit_t kHalfDigitMask = (1ull << kHalfDigitBits) - 1;
// Private helpers for public methods.
Handle<String> ToStringBasePowerOfTwo(Handle<BigInt> x, int radix);
static Handle<BigInt> Copy(Handle<BigInt> source);
void RightTrim();
static Handle<BigInt> AbsoluteAdd(Handle<BigInt> x, Handle<BigInt> y,
bool result_sign);
static Handle<BigInt> AbsoluteSub(Handle<BigInt> x, Handle<BigInt> y,
bool result_sign);
// Returns a positive value if abs(x) > abs(y), a negative value if
// abs(x) < abs(y), or zero if abs(x) == abs(y).
static int AbsoluteCompare(Handle<BigInt> x, Handle<BigInt> y);
static Handle<String> ToStringBasePowerOfTwo(Handle<BigInt> x, int radix);
// Digit arithmetic helpers.
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);
class LengthBits : public BitField<int, 0, kMaxLengthBits> {};
class SignBits : public BitField<bool, LengthBits::kNext, 1> {};