Commit 98bb0673 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Proper variable-length object layout

Bug: v8:6791
Change-Id: I2da258f7db6c74d764c674eb8d550418a566c5ea
Reviewed-on: https://chromium-review.googlesource.com/662138
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48002}
parent 7b5a4022
......@@ -1396,11 +1396,18 @@ Handle<HeapNumber> Factory::NewHeapNumber(MutableMode mode,
HeapNumber);
}
Handle<BigInt> Factory::NewBigInt(PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->AllocateBigInt(pretenure),
Handle<BigInt> Factory::NewBigInt(int length, PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(isolate(),
isolate()->heap()->AllocateBigInt(length, true, pretenure),
BigInt);
}
Handle<BigInt> Factory::NewBigIntRaw(int length, PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(
isolate(), isolate()->heap()->AllocateBigInt(length, false, pretenure),
BigInt);
}
Handle<Object> Factory::NewError(Handle<JSFunction> constructor,
MessageTemplate::Template template_index,
Handle<Object> arg0, Handle<Object> arg1,
......
......@@ -474,7 +474,11 @@ class V8_EXPORT_PRIVATE Factory final {
Handle<HeapNumber> NewHeapNumber(MutableMode mode,
PretenureFlag pretenure = NOT_TENURED);
Handle<BigInt> NewBigInt(PretenureFlag pretenure = NOT_TENURED);
// Allocates a new BigInt with {length} digits and zero-initializes them.
Handle<BigInt> NewBigInt(int length, PretenureFlag pretenure = NOT_TENURED);
// Initializes length and sign fields, but leaves digits uninitialized.
Handle<BigInt> NewBigIntRaw(int length,
PretenureFlag pretenure = NOT_TENURED);
Handle<JSWeakMap> NewJSWeakMap();
......
......@@ -2468,16 +2468,20 @@ AllocationResult Heap::AllocateHeapNumber(MutableMode mode,
return result;
}
AllocationResult Heap::AllocateBigInt(PretenureFlag pretenure) {
STATIC_ASSERT(BigInt::kSize <= kMaxRegularHeapObjectSize);
AllocationResult Heap::AllocateBigInt(int length, bool zero_initialize,
PretenureFlag pretenure) {
if (length < 0 || length > BigInt::kMaxLength) {
v8::internal::Heap::FatalProcessOutOfMemory("invalid BigInt length", true);
}
int size = BigInt::SizeFor(length);
AllocationSpace space = SelectSpace(pretenure);
HeapObject* result = nullptr;
{
AllocationSpace space = SelectSpace(pretenure);
AllocationResult allocation = AllocateRaw(BigInt::kSize, space);
AllocationResult allocation = AllocateRaw(size, space);
if (!allocation.To(&result)) return allocation;
}
result->set_map_after_allocation(bigint_map(), SKIP_WRITE_BARRIER);
BigInt::cast(result)->Initialize(length, zero_initialize);
return result;
}
......
......@@ -1982,7 +1982,9 @@ class Heap {
MUST_USE_RESULT AllocationResult AllocateHeapNumber(
MutableMode mode = IMMUTABLE, PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT AllocationResult AllocateBigInt(PretenureFlag pretenure);
MUST_USE_RESULT AllocationResult AllocateBigInt(int length,
bool zero_initialize,
PretenureFlag pretenure);
// Allocates a byte array of the specified length
MUST_USE_RESULT AllocationResult
......
......@@ -15,8 +15,11 @@
namespace v8 {
namespace internal {
class BigInt;
#define TYPED_VISITOR_ID_LIST(V) \
V(AllocationSite) \
V(BigInt) \
V(ByteArray) \
V(BytecodeArray) \
V(Cell) \
......
......@@ -229,7 +229,7 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, termination_exception);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, optimized_out);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, stale_register);
ALLOCATE_MAP(BIGINT_TYPE, BigInt::kSize, bigint);
ALLOCATE_VARSIZE_MAP(BIGINT_TYPE, bigint);
for (unsigned i = 0; i < arraysize(string_type_table); i++) {
const StringTypeTable& entry = string_type_table[i];
......
......@@ -214,6 +214,19 @@ class BytecodeArray::BodyDescriptor final : public BodyDescriptorBase {
}
};
class BigInt::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(HeapObject* obj, int offset) { return false; }
template <typename ObjectVisitor>
static inline void IterateBody(HeapObject* obj, int object_size,
ObjectVisitor* v) {}
static inline int SizeOf(Map* map, HeapObject* obj) {
return BigInt::SizeFor(BigInt::cast(obj)->length());
}
};
class FixedDoubleArray::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(HeapObject* obj, int offset) { return false; }
......
......@@ -3264,6 +3264,9 @@ int HeapObject::SizeFromMap(Map* map) const {
return FeedbackVector::SizeFor(
reinterpret_cast<const FeedbackVector*>(this)->length());
}
if (instance_type == BIGINT_TYPE) {
return BigInt::SizeFor(reinterpret_cast<const BigInt*>(this)->length());
}
DCHECK(instance_type == CODE_TYPE);
return reinterpret_cast<const Code*>(this)->CodeSize();
}
......
......@@ -256,7 +256,7 @@ MaybeHandle<String> Object::ConvertToString(Isolate* isolate,
String);
}
if (input->IsBigInt()) {
return BigInt::ToString(Handle<BigInt>::cast(input));
return BigInt::ToString(Handle<BigInt>::cast(input), 10);
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input),
......@@ -2358,7 +2358,7 @@ Object* GetSimpleHash(Object* object) {
return Smi::FromInt(hash);
}
if (object->IsBigInt()) {
uint32_t hash = ComputeIntegerHash(BigInt::cast(object)->value());
uint32_t hash = BigInt::cast(object)->Hash();
return Smi::FromInt(hash & Smi::kMaxValue);
}
DCHECK(object->IsJSReceiver());
......@@ -2413,7 +2413,7 @@ bool Object::SameValue(Object* other) {
return String::cast(this)->Equals(String::cast(other));
}
if (IsBigInt() && other->IsBigInt()) {
return BigInt::cast(this)->Equals(BigInt::cast(other));
return BigInt::Equal(BigInt::cast(this), BigInt::cast(other));
}
return false;
}
......@@ -3168,9 +3168,11 @@ VisitorId Map::GetVisitorId(Map* map) {
case FOREIGN_TYPE:
case HEAP_NUMBER_TYPE:
case MUTABLE_HEAP_NUMBER_TYPE:
case BIGINT_TYPE:
return kVisitDataObject;
case BIGINT_TYPE:
return kVisitBigInt;
case FIXED_UINT8_ARRAY_TYPE:
case FIXED_INT8_ARRAY_TYPE:
case FIXED_UINT16_ARRAY_TYPE:
......
......@@ -15,7 +15,35 @@
namespace v8 {
namespace internal {
SMI_ACCESSORS(BigInt, value, kValueOffset)
int BigInt::length() const {
intptr_t bitfield = READ_INTPTR_FIELD(this, kBitfieldOffset);
return LengthBits::decode(static_cast<uint32_t>(bitfield));
}
void BigInt::set_length(int new_length) {
intptr_t bitfield = READ_INTPTR_FIELD(this, kBitfieldOffset);
bitfield = LengthBits::update(static_cast<uint32_t>(bitfield), new_length);
WRITE_INTPTR_FIELD(this, kBitfieldOffset, bitfield);
}
bool BigInt::sign() const {
intptr_t bitfield = READ_INTPTR_FIELD(this, kBitfieldOffset);
return SignBits::decode(static_cast<uint32_t>(bitfield));
}
void BigInt::set_sign(bool new_sign) {
intptr_t bitfield = READ_INTPTR_FIELD(this, kBitfieldOffset);
bitfield = SignBits::update(static_cast<uint32_t>(bitfield), new_sign);
WRITE_INTPTR_FIELD(this, kBitfieldOffset, bitfield);
}
BigInt::digit_t BigInt::digit(int n) const {
const byte* address = FIELD_ADDR_CONST(this, kDigitsOffset + n * kDigitSize);
return *reinterpret_cast<digit_t*>(reinterpret_cast<intptr_t>(address));
}
void BigInt::set_digit(int n, digit_t value) {
byte* address = FIELD_ADDR(this, kDigitsOffset + n * kDigitSize);
(*reinterpret_cast<digit_t*>(reinterpret_cast<intptr_t>(address))) = value;
}
TYPE_CHECKER(BigInt, BIGINT_TYPE)
} // namespace internal
......
......@@ -9,25 +9,120 @@
namespace v8 {
namespace internal {
Handle<BigInt> BigInt::UnaryMinus(Handle<BigInt> x) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::BitwiseNot(Handle<BigInt> x) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
MaybeHandle<BigInt> BigInt::Exponentiate(Handle<BigInt> base,
Handle<BigInt> exponent) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::Multiply(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
MaybeHandle<BigInt> BigInt::Divide(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
MaybeHandle<BigInt> BigInt::Remainder(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::Add(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::Subtract(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::LeftShift(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::SignedRightShift(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
MaybeHandle<BigInt> BigInt::UnsignedRightShift(Handle<BigInt> x,
Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
bool BigInt::LessThan(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
bool BigInt::Equal(BigInt* x, BigInt* y) {
if (x->sign() != y->sign()) return false;
if (x->length() != y->length()) return false;
for (int i = 0; i < x->length(); i++) {
if (x->digit(i) != y->digit(i)) return false;
}
return true;
}
Handle<BigInt> BigInt::BitwiseAnd(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::BitwiseXor(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
// TODO(jkummerow): Implement this properly.
Factory* factory = bigint->GetIsolate()->factory();
if (bigint->is_zero()) return factory->NewStringFromStaticChars("0");
DCHECK(bigint->length() == 1);
int value = static_cast<uint32_t>(bigint->digit(0));
if (bigint->sign()) value = -value; // This can overflow. We don't care.
Handle<Object> number = factory->NewNumberFromInt(value);
return factory->NumberToString(number);
}
void BigInt::Initialize(int length, bool zero_initialize) {
set_length(length);
set_sign(false);
if (zero_initialize) {
memset(reinterpret_cast<void*>(reinterpret_cast<Address>(this) +
kDigitsOffset - kHeapObjectTag),
0, length * kDigitSize);
#if DEBUG
} else {
memset(reinterpret_cast<void*>(reinterpret_cast<Address>(this) +
kDigitsOffset - kHeapObjectTag),
0xbf, length * kDigitSize);
#endif
}
}
#ifdef OBJECT_PRINT
void BigInt::BigIntPrint(std::ostream& os) {
DisallowHeapAllocation no_gc;
HeapObject::PrintHeader(os, "BigInt");
os << "- value: " << value();
os << "\n";
int len = length();
os << "- length: " << len << "\n";
os << "- sign: " << sign() << "\n";
if (len > 0) {
os << "- digits:";
for (int i = 0; i < len; i++) {
os << "\n 0x" << std::hex << digit(i);
}
os << std::dec << "\n";
}
}
#endif // OBJECT_PRINT
bool BigInt::Equals(BigInt* other) const {
DisallowHeapAllocation no_gc;
return value() == other->value();
}
Handle<String> BigInt::ToString(Handle<BigInt> bigint) {
Isolate* isolate = bigint->GetIsolate();
Handle<Object> number = isolate->factory()->NewNumberFromInt(bigint->value());
return isolate->factory()->NumberToString(number);
}
} // namespace internal
} // namespace v8
......@@ -19,19 +19,91 @@ namespace internal {
// Arbitrary precision integers in JavaScript.
class BigInt : public HeapObject {
public:
// Implementation of the Spec methods, see:
// https://tc39.github.io/proposal-bigint/#sec-numeric-types
// Sections 1.1.1 through 1.1.19.
static Handle<BigInt> UnaryMinus(Handle<BigInt> x);
static Handle<BigInt> BitwiseNot(Handle<BigInt> x);
static MaybeHandle<BigInt> Exponentiate(Handle<BigInt> base,
Handle<BigInt> exponent);
static Handle<BigInt> Multiply(Handle<BigInt> x, Handle<BigInt> y);
static MaybeHandle<BigInt> Divide(Handle<BigInt> x, Handle<BigInt> y);
static MaybeHandle<BigInt> Remainder(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> Add(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> Subtract(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> LeftShift(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> SignedRightShift(Handle<BigInt> x, Handle<BigInt> y);
static MaybeHandle<BigInt> UnsignedRightShift(Handle<BigInt> x,
Handle<BigInt> y);
static bool LessThan(Handle<BigInt> x, Handle<BigInt> y);
static bool Equal(BigInt* x, BigInt* y);
static Handle<BigInt> BitwiseAnd(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseXor(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseOr(Handle<BigInt> x, Handle<BigInt> y);
// Other parts of the public interface.
bool ToBoolean() { return !is_zero(); }
uint32_t Hash() {
// TODO(jkummerow): Improve this. At least use length and sign.
return is_zero() ? 0 : ComputeIntegerHash(static_cast<uint32_t>(digit(0)));
}
DECL_CAST(BigInt)
DECL_VERIFIER(BigInt)
DECL_PRINTER(BigInt)
DECL_INT_ACCESSORS(value)
// TODO(jkummerow): Do we need {synchronized_length} for GC purposes?
DECL_INT_ACCESSORS(length)
inline static int SizeFor(int length) {
return kHeaderSize + length * kDigitSize;
}
void Initialize(int length, bool zero_initialize);
static Handle<String> ToString(Handle<BigInt> bigint, int radix);
static const int kValueOffset = HeapObject::kHeaderSize;
static const int kSize = kValueOffset + kPointerSize;
// Temporarily exposed helper, pending proper initialization.
void set_value(int value) {
DCHECK(length() == 1);
if (value > 0) {
set_digit(0, value);
} else {
set_digit(0, -value); // This can overflow. We don't care.
set_sign(true);
}
}
bool Equals(BigInt* other) const;
static Handle<String> ToString(Handle<BigInt> bigint);
// The maximum length that the current implementation supports would be
// kMaxInt / kDigitBits. However, we use a lower limit for now, because
// raising it later is easier than lowering it.
static const int kMaxLengthBits = 20;
static const int kMaxLength = (1 << kMaxLengthBits) - 1;
class BodyDescriptor;
private:
typedef uintptr_t digit_t;
static const int kDigitSize = sizeof(digit_t);
static const int kDigitBits = kDigitSize * kBitsPerByte;
static const int kHalfDigitBits = kDigitBits / 2;
static const digit_t kHalfDigitMask = (1ull << kHalfDigitBits) - 1;
class LengthBits : public BitField<int, 0, kMaxLengthBits> {};
class SignBits : public BitField<bool, LengthBits::kNext, 1> {};
// Low-level accessors.
// sign() == true means negative.
DECL_BOOLEAN_ACCESSORS(sign)
inline digit_t digit(int n) const;
inline void set_digit(int n, digit_t value);
bool is_zero() {
DCHECK(length() > 0 || !sign()); // There is no -0n.
return length() == 0;
}
static const int kBitfieldOffset = HeapObject::kHeaderSize;
static const int kDigitsOffset = kBitfieldOffset + kPointerSize;
static const int kHeaderSize = kDigitsOffset;
DISALLOW_IMPLICIT_CONSTRUCTORS(BigInt);
};
......
......@@ -17,6 +17,7 @@ namespace internal {
#define VISITOR_ID_LIST(V) \
V(AllocationSite) \
V(BigInt) \
V(ByteArray) \
V(BytecodeArray) \
V(Cell) \
......
......@@ -7,6 +7,7 @@
#include "src/arguments.h"
#include "src/counters.h"
#include "src/objects-inl.h"
#include "src/objects/bigint.h"
namespace v8 {
namespace internal {
......@@ -26,7 +27,9 @@ RUNTIME_FUNCTION(Runtime_BigInt) {
NewTypeError(MessageTemplate::kUnsupported));
}
Handle<BigInt> result = isolate->factory()->NewBigInt();
if (value == 0) return *isolate->factory()->NewBigInt(0);
Handle<BigInt> result = isolate->factory()->NewBigInt(1);
result->set_value(value);
return *result;
}
......@@ -37,7 +40,7 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) {
CONVERT_ARG_HANDLE_CHECKED(Object, lhs, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, rhs, 1);
bool result = lhs->IsBigInt() && rhs->IsBigInt() &&
BigInt::cast(*lhs)->Equals(BigInt::cast(*rhs));
BigInt::Equal(BigInt::cast(*lhs), BigInt::cast(*rhs));
return *isolate->factory()->ToBoolean(result);
}
......@@ -45,7 +48,7 @@ RUNTIME_FUNCTION(Runtime_BigIntToBoolean) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(BigInt, bigint, 0);
return *isolate->factory()->ToBoolean(bigint->value());
return *isolate->factory()->ToBoolean(bigint->ToBoolean());
}
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment