From 4de3bb500c4d4c482ee63924c956c404b06960c1 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Thu, 9 Jun 2011 10:03:35 +0000 Subject: [PATCH] Implement core support for FixedDoubleArrays. Under a flag without IC or Crankshaft support. BUG=none TEST=none Review URL: http://codereview.chromium.org/7089002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8229 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 2 +- src/factory.cc | 12 +- src/factory.h | 9 +- src/flag-definitions.h | 3 + src/heap.cc | 75 +++++++++ src/heap.h | 16 ++ src/mark-compact.cc | 2 + src/objects-debug.cc | 18 ++- src/objects-inl.h | 138 +++++++++++++++- src/objects-visiting.cc | 5 +- src/objects-visiting.h | 10 +- src/objects.cc | 311 ++++++++++++++++++++++++++++++++---- src/objects.h | 131 +++++++++++++-- src/spaces.cc | 2 +- src/v8-counters.h | 5 +- test/mjsunit/unbox-double-arrays.js | 78 +++++++++ 16 files changed, 757 insertions(+), 60 deletions(-) create mode 100644 test/mjsunit/unbox-double-arrays.js diff --git a/include/v8.h b/include/v8.h index eb677aa..443e064 100644 --- a/include/v8.h +++ b/include/v8.h @@ -3726,7 +3726,7 @@ class Internals { static const int kFullStringRepresentationMask = 0x07; static const int kExternalTwoByteRepresentationTag = 0x02; - static const int kJSObjectType = 0xa2; + static const int kJSObjectType = 0xa3; static const int kFirstNonstringType = 0x80; static const int kForeignType = 0x85; diff --git a/src/factory.cc b/src/factory.cc index 55d6ff0..c7bdef7 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -58,6 +58,16 @@ Handle Factory::NewFixedArrayWithHoles(int size, } +Handle Factory::NewFixedDoubleArray(int size, + PretenureFlag pretenure) { + ASSERT(0 <= size); + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure), + FixedArray); +} + + Handle Factory::NewStringDictionary(int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), diff --git a/src/factory.h b/src/factory.h index 55d1e9a..5ea7e33 100644 --- a/src/factory.h +++ b/src/factory.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -39,7 +39,7 @@ namespace internal { class Factory { public: - // Allocate a new fixed array with undefined entries. + // Allocate a new uninitialized fixed array. Handle NewFixedArray( int size, PretenureFlag pretenure = NOT_TENURED); @@ -49,6 +49,11 @@ class Factory { int size, PretenureFlag pretenure = NOT_TENURED); + // Allocate a new uninitialized fixed double array. + Handle NewFixedDoubleArray( + int size, + PretenureFlag pretenure = NOT_TENURED); + Handle NewNumberDictionary(int at_least_space_for); Handle NewStringDictionary(int at_least_space_for); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 8b70995..ec72148 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -99,6 +99,9 @@ private: // Flags for experimental language features. DEFINE_bool(harmony_proxies, false, "enable harmony proxies") +// Flags for experimental implementation features. +DEFINE_bool(unbox_double_arrays, false, "automatically unbox arrays of doubles") + // Flags for Crankshaft. #ifdef V8_TARGET_ARCH_MIPS DEFINE_bool(crankshaft, false, "use crankshaft") diff --git a/src/heap.cc b/src/heap.cc index 8f1ad94..e047c96 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1286,6 +1286,7 @@ class ScavengingVisitor : public StaticVisitorBase { table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate); table_.Register(kVisitByteArray, &EvacuateByteArray); table_.Register(kVisitFixedArray, &EvacuateFixedArray); + table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray); table_.Register(kVisitGlobalContext, &ObjectEvacuationStrategy:: @@ -1433,6 +1434,18 @@ class ScavengingVisitor : public StaticVisitorBase { } + static inline void EvacuateFixedDoubleArray(Map* map, + HeapObject** slot, + HeapObject* object) { + int length = reinterpret_cast(object)->length(); + int object_size = FixedDoubleArray::SizeFor(length); + EvacuateObject(map, + slot, + object, + object_size); + } + + static inline void EvacuateByteArray(Map* map, HeapObject** slot, HeapObject* object) { @@ -1772,6 +1785,12 @@ bool Heap::CreateInitialMaps() { Map::cast(obj)->set_is_undetectable(); { MaybeObject* maybe_obj = + AllocateMap(FIXED_DOUBLE_ARRAY_TYPE, kVariableSizeSentinel); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_fixed_double_array_map(Map::cast(obj)); + + { MaybeObject* maybe_obj = AllocateMap(BYTE_ARRAY_TYPE, kVariableSizeSentinel); if (!maybe_obj->ToObject(&obj)) return false; } @@ -3812,6 +3831,62 @@ MaybeObject* Heap::AllocateUninitializedFixedArray(int length) { } +MaybeObject* Heap::AllocateEmptyFixedDoubleArray() { + int size = FixedDoubleArray::SizeFor(0); + Object* result; + { MaybeObject* maybe_result = + AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE); + if (!maybe_result->ToObject(&result)) return maybe_result; + } + // Initialize the object. + reinterpret_cast(result)->set_map( + fixed_double_array_map()); + reinterpret_cast(result)->set_length(0); + return result; +} + + +MaybeObject* Heap::AllocateUninitializedFixedDoubleArray( + int length, + PretenureFlag pretenure) { + if (length == 0) return empty_fixed_double_array(); + + Object* obj; + { MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(length, pretenure); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + + reinterpret_cast(obj)->set_map(fixed_double_array_map()); + FixedDoubleArray::cast(obj)->set_length(length); + return obj; +} + + +MaybeObject* Heap::AllocateRawFixedDoubleArray(int length, + PretenureFlag pretenure) { + if (length < 0 || length > FixedDoubleArray::kMaxLength) { + return Failure::OutOfMemoryException(); + } + + AllocationSpace space = + (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE; + int size = FixedDoubleArray::SizeFor(length); + if (space == NEW_SPACE && size > kMaxObjectSizeInNewSpace) { + // Too big for new space. + space = LO_SPACE; + } else if (space == OLD_DATA_SPACE && + size > MaxObjectSizeInPagedSpace()) { + // Too big for old data space. + space = LO_SPACE; + } + + AllocationSpace retry_space = + (size <= MaxObjectSizeInPagedSpace()) ? OLD_DATA_SPACE : LO_SPACE; + + return AllocateRaw(size, space, retry_space); +} + + MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) { Object* result; { MaybeObject* maybe_result = AllocateFixedArray(length, pretenure); diff --git a/src/heap.h b/src/heap.h index f97344b..664cce8 100644 --- a/src/heap.h +++ b/src/heap.h @@ -66,6 +66,7 @@ inline Heap* _inline_get_heap_(); V(Map, global_context_map, GlobalContextMap) \ V(Map, fixed_array_map, FixedArrayMap) \ V(Map, fixed_cow_array_map, FixedCOWArrayMap) \ + V(Map, fixed_double_array_map, FixedDoubleArrayMap) \ V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \ V(Map, meta_map, MetaMap) \ V(Map, hash_table_map, HashTableMap) \ @@ -78,6 +79,7 @@ inline Heap* _inline_get_heap_(); V(Object, termination_exception, TerminationException) \ V(FixedArray, empty_fixed_array, EmptyFixedArray) \ V(ByteArray, empty_byte_array, EmptyByteArray) \ + V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \ V(String, empty_string, EmptyString) \ V(DescriptorArray, empty_descriptor_array, EmptyDescriptorArray) \ V(Map, string_map, StringMap) \ @@ -620,6 +622,17 @@ class Heap { int length, PretenureFlag pretenure = NOT_TENURED); + MUST_USE_RESULT MaybeObject* AllocateRawFixedDoubleArray( + int length, + PretenureFlag pretenure); + + // Allocates a fixed double array with uninitialized values. Returns + // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed. + // Please note this does not perform a garbage collection. + MUST_USE_RESULT MaybeObject* AllocateUninitializedFixedDoubleArray( + int length, + PretenureFlag pretenure = NOT_TENURED); + // AllocateHashTable is identical to AllocateFixedArray except // that the resulting object has hash_table_map as map. MUST_USE_RESULT MaybeObject* AllocateHashTable( @@ -1460,6 +1473,9 @@ class Heap { // Allocate empty fixed array. MUST_USE_RESULT MaybeObject* AllocateEmptyFixedArray(); + // Allocate empty fixed double array. + MUST_USE_RESULT MaybeObject* AllocateEmptyFixedDoubleArray(); + void SwitchScavengingVisitorsTableIfProfilingWasEnabled(); // Performs a minor collection in new generation. diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 2f45227..e19d891 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -396,6 +396,8 @@ class StaticMarkingVisitor : public StaticVisitorBase { FixedArray::BodyDescriptor, void>::Visit); + table_.Register(kVisitFixedDoubleArray, DataObjectVisitor::Visit); + table_.Register(kVisitGlobalContext, &FixedBodyVisitorFixedArrayVerify(); break; + case FIXED_DOUBLE_ARRAY_TYPE: + FixedDoubleArray::cast(this)->FixedDoubleArrayVerify(); + break; case BYTE_ARRAY_TYPE: ByteArray::cast(this)->ByteArrayVerify(); break; @@ -307,6 +310,17 @@ void FixedArray::FixedArrayVerify() { } +void FixedDoubleArray::FixedDoubleArrayVerify() { + for (int i = 0; i < length(); i++) { + if (!is_the_hole(i)) { + double value = get(i); + ASSERT(!isnan(value) || + BitCast(value) == kCanonicalNonHoleNanInt64); + } + } +} + + void JSValue::JSValueVerify() { Object* v = value(); if (v->IsHeapObject()) { @@ -432,7 +446,9 @@ void Code::CodeVerify() { void JSArray::JSArrayVerify() { JSObjectVerify(); ASSERT(length()->IsNumber() || length()->IsUndefined()); - ASSERT(elements()->IsUndefined() || elements()->IsFixedArray()); + ASSERT(elements()->IsUndefined() || + elements()->IsFixedArray() || + elements()->IsFixedDoubleArray()); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 72dca22..f03021e 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -217,6 +217,10 @@ bool Object::IsExternalTwoByteString() { String::cast(this)->IsTwoByteRepresentation(); } +bool Object::HasValidElements() { + // Dictionary is covered under FixedArray. + return IsFixedArray() || IsFixedDoubleArray() || IsExternalArray(); +} StringShape::StringShape(String* str) : type_(str->map()->instance_type()) { @@ -489,6 +493,13 @@ bool Object::IsFixedArray() { } +bool Object::IsFixedDoubleArray() { + return Object::IsHeapObject() + && HeapObject::cast(this)->map()->instance_type() == + FIXED_DOUBLE_ARRAY_TYPE; +} + + bool Object::IsDescriptorArray() { return IsFixedArray(); } @@ -1318,8 +1329,7 @@ ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset) HeapObject* JSObject::elements() { Object* array = READ_FIELD(this, kElementsOffset); - // In the assert below Dictionary is covered under FixedArray. - ASSERT(array->IsFixedArray() || array->IsExternalArray()); + ASSERT(array->HasValidElements()); return reinterpret_cast(array); } @@ -1328,8 +1338,7 @@ void JSObject::set_elements(HeapObject* value, WriteBarrierMode mode) { ASSERT(map()->has_fast_elements() == (value->map() == GetHeap()->fixed_array_map() || value->map() == GetHeap()->fixed_cow_array_map())); - // In the assert below Dictionary is covered under FixedArray. - ASSERT(value->IsFixedArray() || value->IsExternalArray()); + ASSERT(value->HasValidElements()); WRITE_FIELD(this, kElementsOffset, value); CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, mode); } @@ -1577,6 +1586,12 @@ bool Object::IsStringObjectWithCharacterAt(uint32_t index) { } +FixedArrayBase* FixedArrayBase::cast(Object* object) { + ASSERT(object->IsFixedArray() || object->IsFixedDoubleArray()); + return reinterpret_cast(object); +} + + Object* FixedArray::get(int index) { ASSERT(index >= 0 && index < this->length()); return READ_FIELD(this, kHeaderSize + index * kPointerSize); @@ -1600,6 +1615,88 @@ void FixedArray::set(int index, Object* value) { } +double FixedDoubleArray::get(int index) { + ASSERT(map() != HEAP->fixed_cow_array_map() && + map() != HEAP->fixed_array_map()); + ASSERT(index >= 0 && index < this->length()); + double result = READ_DOUBLE_FIELD(this, kHeaderSize + index * kDoubleSize); + ASSERT(!is_the_hole_nan(result)); + return result; +} + + +void FixedDoubleArray::set(int index, double value) { + ASSERT(map() != HEAP->fixed_cow_array_map() && + map() != HEAP->fixed_array_map()); + int offset = kHeaderSize + index * kDoubleSize; + if (isnan(value)) value = canonical_not_the_hole_nan_as_double(); + WRITE_DOUBLE_FIELD(this, offset, value); +} + + +void FixedDoubleArray::set_the_hole(int index) { + ASSERT(map() != HEAP->fixed_cow_array_map() && + map() != HEAP->fixed_array_map()); + int offset = kHeaderSize + index * kDoubleSize; + WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); +} + + +bool FixedDoubleArray::is_the_hole(int index) { + int offset = kHeaderSize + index * kDoubleSize; + return is_the_hole_nan(READ_DOUBLE_FIELD(this, offset)); +} + + +void FixedDoubleArray::Initialize(FixedDoubleArray* from) { + int old_length = from->length(); + ASSERT(old_length < length()); + OS::MemCopy(FIELD_ADDR(this, kHeaderSize), + FIELD_ADDR(from, kHeaderSize), + old_length * kDoubleSize); + int offset = kHeaderSize + old_length * kDoubleSize; + for (int current = from->length(); current < length(); ++current) { + WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); + offset += kDoubleSize; + } +} + + +void FixedDoubleArray::Initialize(FixedArray* from) { + int old_length = from->length(); + ASSERT(old_length < length()); + for (int i = 0; i < old_length; i++) { + Object* hole_or_object = from->get(i); + if (hole_or_object->IsTheHole()) { + set_the_hole(i); + } else { + set(i, hole_or_object->Number()); + } + } + int offset = kHeaderSize + old_length * kDoubleSize; + for (int current = from->length(); current < length(); ++current) { + WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); + offset += kDoubleSize; + } +} + + +void FixedDoubleArray::Initialize(NumberDictionary* from) { + int offset = kHeaderSize; + for (int current = 0; current < length(); ++current) { + WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); + offset += kDoubleSize; + } + for (int i = 0; i < from->Capacity(); i++) { + Object* key = from->KeyAt(i); + if (key->IsNumber()) { + uint32_t entry = static_cast(key->Number()); + set(entry, from->ValueAt(i)->Number()); + } + } +} + + WriteBarrierMode HeapObject::GetWriteBarrierMode(const AssertNoAllocation&) { if (GetHeap()->InNewSpace(this)) return SKIP_WRITE_BARRIER; return UPDATE_WRITE_BARRIER; @@ -1900,6 +1997,7 @@ void NumberDictionary::set_requires_slow_elements() { CAST_ACCESSOR(FixedArray) +CAST_ACCESSOR(FixedDoubleArray) CAST_ACCESSOR(DescriptorArray) CAST_ACCESSOR(DeoptimizationInputData) CAST_ACCESSOR(DeoptimizationOutputData) @@ -1964,7 +2062,7 @@ HashTable* HashTable::cast(Object* obj) { } -SMI_ACCESSORS(FixedArray, length, kLengthOffset) +SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset) SMI_ACCESSORS(ByteArray, length, kLengthOffset) INT_ACCESSORS(ExternalArray, length, kLengthOffset) @@ -2423,6 +2521,10 @@ int HeapObject::SizeFromMap(Map* map) { return SeqTwoByteString::SizeFor( reinterpret_cast(this)->length()); } + if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) { + return FixedDoubleArray::SizeFor( + reinterpret_cast(this)->length()); + } ASSERT(instance_type == CODE_TYPE); return reinterpret_cast(this)->CodeSize(); } @@ -2981,20 +3083,33 @@ MaybeObject* Map::GetFastElementsMap() { } Map* new_map = Map::cast(obj); new_map->set_elements_kind(JSObject::FAST_ELEMENTS); - isolate()->counters()->map_slow_to_fast_elements()->Increment(); + isolate()->counters()->map_to_fast_elements()->Increment(); + return new_map; +} + + +MaybeObject* Map::GetFastDoubleElementsMap() { + if (has_fast_double_elements()) return this; + Object* obj; + { MaybeObject* maybe_obj = CopyDropTransitions(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + Map* new_map = Map::cast(obj); + new_map->set_elements_kind(JSObject::FAST_DOUBLE_ELEMENTS); + isolate()->counters()->map_to_fast_double_elements()->Increment(); return new_map; } MaybeObject* Map::GetSlowElementsMap() { - if (!has_fast_elements()) return this; + if (!has_fast_elements() && !has_fast_double_elements()) return this; Object* obj; { MaybeObject* maybe_obj = CopyDropTransitions(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); new_map->set_elements_kind(JSObject::DICTIONARY_ELEMENTS); - isolate()->counters()->map_fast_to_slow_elements()->Increment(); + isolate()->counters()->map_to_slow_elements()->Increment(); return new_map; } @@ -3788,6 +3903,8 @@ JSObject::ElementsKind JSObject::GetElementsKind() { ASSERT((kind == FAST_ELEMENTS && (elements()->map() == GetHeap()->fixed_array_map() || elements()->map() == GetHeap()->fixed_cow_array_map())) || + (kind == FAST_DOUBLE_ELEMENTS && + elements()->IsFixedDoubleArray()) || (kind == DICTIONARY_ELEMENTS && elements()->IsFixedArray() && elements()->IsDictionary()) || @@ -3801,6 +3918,11 @@ bool JSObject::HasFastElements() { } +bool JSObject::HasFastDoubleElements() { + return GetElementsKind() == FAST_DOUBLE_ELEMENTS; +} + + bool JSObject::HasDictionaryElements() { return GetElementsKind() == DICTIONARY_ELEMENTS; } diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc index a03c8c8..3b36cbe 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -73,6 +73,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case FIXED_ARRAY_TYPE: return kVisitFixedArray; + case FIXED_DOUBLE_ARRAY_TYPE: + return kVisitFixedDoubleArray; + case ODDBALL_TYPE: return kVisitOddball; diff --git a/src/objects-visiting.h b/src/objects-visiting.h index d06140c..f2b8586 100644 --- a/src/objects-visiting.h +++ b/src/objects-visiting.h @@ -1,4 +1,4 @@ -// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -52,6 +52,7 @@ class StaticVisitorBase : public AllStatic { kVisitShortcutCandidate, kVisitByteArray, kVisitFixedArray, + kVisitFixedDoubleArray, kVisitGlobalContext, // For data objects, JS objects and structs along with generic visitor which @@ -285,6 +286,8 @@ class StaticNewSpaceVisitor : public StaticVisitorBase { FixedArray::BodyDescriptor, int>::Visit); + table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray); + table_.Register(kVisitGlobalContext, &FixedBodyVisitor(object)->ByteArraySize(); } + static inline int VisitFixedDoubleArray(Map* map, HeapObject* object) { + int length = reinterpret_cast(object)->length(); + return FixedDoubleArray::SizeFor(length); + } + static inline int VisitSeqAsciiString(Map* map, HeapObject* object) { return SeqAsciiString::cast(object)-> SeqAsciiStringSize(map->instance_type()); diff --git a/src/objects.cc b/src/objects.cc index fef784c..610301f 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -58,6 +58,10 @@ namespace internal { const int kGetterIndex = 0; const int kSetterIndex = 1; +uint64_t FixedDoubleArray::kHoleNanInt64 = -1; +uint64_t FixedDoubleArray::kCanonicalNonHoleNanLower32 = 0x7FF00000; +uint64_t FixedDoubleArray::kCanonicalNonHoleNanInt64 = + kCanonicalNonHoleNanLower32 << 32; MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, Object* value) { @@ -1178,6 +1182,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case FIXED_ARRAY_TYPE: FixedArray::BodyDescriptor::IterateBody(this, object_size, v); break; + case FIXED_DOUBLE_ARRAY_TYPE: + break; case JS_OBJECT_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_VALUE_TYPE: @@ -2800,7 +2806,7 @@ MaybeObject* JSObject::NormalizeElements() { ASSERT(!HasExternalArrayElements()); if (HasDictionaryElements()) return this; Map* old_map = map(); - ASSERT(old_map->has_fast_elements()); + ASSERT(old_map->has_fast_elements() || old_map->has_fast_double_elements()); Object* obj; { MaybeObject* maybe_obj = old_map->GetSlowElementsMap(); @@ -2809,7 +2815,7 @@ MaybeObject* JSObject::NormalizeElements() { Map* new_map = Map::cast(obj); // Get number of entries. - FixedArray* array = FixedArray::cast(elements()); + FixedArrayBase* array = FixedArrayBase::cast(elements()); // Compute the effective length. int length = IsJSArray() ? @@ -2818,17 +2824,35 @@ MaybeObject* JSObject::NormalizeElements() { { MaybeObject* maybe_obj = NumberDictionary::Allocate(length); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } + bool has_double_elements = old_map->has_fast_double_elements(); NumberDictionary* dictionary = NumberDictionary::cast(obj); // Copy entries. for (int i = 0; i < length; i++) { - Object* value = array->get(i); + Object* value = NULL; + if (has_double_elements) { + FixedDoubleArray* double_array = FixedDoubleArray::cast(array); + if (double_array->is_the_hole(i)) { + value = GetIsolate()->heap()->the_hole_value(); + } else { + // Objects must be allocated in the old object space, since the + // overall number of HeapNumbers needed for the conversion might + // exceed the capacity of new space, and we would fail repeatedly + // trying to convert the FixedDoubleArray. + MaybeObject* maybe_value_object = + GetHeap()->AllocateHeapNumber(double_array->get(i), TENURED); + if (!maybe_value_object->ToObject(&value)) return maybe_value_object; + } + } else { + ASSERT(old_map->has_fast_elements()); + FixedArray* fixed_array = FixedArray::cast(array); + value = fixed_array->get(i); + } + PropertyDetails details = PropertyDetails(NONE, NORMAL); if (!value->IsTheHole()) { - PropertyDetails details = PropertyDetails(NONE, NORMAL); Object* result; - { MaybeObject* maybe_result = - dictionary->AddNumberEntry(i, array->get(i), details); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + MaybeObject* maybe_result = + dictionary->AddNumberEntry(i, value, details); + if (!maybe_result->ToObject(&result)) return maybe_result; dictionary = NumberDictionary::cast(result); } } @@ -2998,14 +3022,23 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { { MaybeObject* maybe_obj = EnsureWritableFastElements(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - uint32_t length = IsJSArray() ? - static_cast(Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast(FixedArray::cast(elements())->length()); - if (index < length) { + int length = IsJSArray() + ? Smi::cast(JSArray::cast(this)->length())->value() + : FixedArray::cast(elements())->length(); + if (index < static_cast(length)) { FixedArray::cast(elements())->set_the_hole(index); } break; } + case FAST_DOUBLE_ELEMENTS: { + int length = IsJSArray() + ? Smi::cast(JSArray::cast(this)->length())->value() + : FixedArray::cast(elements())->length(); + if (index < static_cast(length)) { + FixedDoubleArray::cast(elements())->set_the_hole(index); + } + break; + } case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: @@ -7098,10 +7131,10 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, } Map* new_map = Map::cast(obj); - AssertNoAllocation no_gc; - WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc); switch (GetElementsKind()) { case FAST_ELEMENTS: { + AssertNoAllocation no_gc; + WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc); FixedArray* old_elements = FixedArray::cast(elements()); uint32_t old_length = static_cast(old_elements->length()); // Fill out the new array with this content and array holes. @@ -7110,7 +7143,32 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, } break; } + case FAST_DOUBLE_ELEMENTS: { + FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements()); + uint32_t old_length = static_cast(old_elements->length()); + // Fill out the new array with this content and array holes. + for (uint32_t i = 0; i < old_length; i++) { + if (!old_elements->is_the_hole(i)) { + Object* obj; + // Objects must be allocated in the old object space, since the + // overall number of HeapNumbers needed for the conversion might + // exceed the capacity of new space, and we would fail repeatedly + // trying to convert the FixedDoubleArray. + MaybeObject* maybe_value_object = + GetHeap()->AllocateHeapNumber(old_elements->get(i), TENURED); + if (!maybe_value_object->ToObject(&obj)) return maybe_value_object; + // Force write barrier. It's not worth trying to exploit + // elems->GetWriteBarrierMode(), since it requires an + // AssertNoAllocation stack object that would have to be positioned + // after the HeapNumber allocation anyway. + elems->set(i, obj, UPDATE_WRITE_BARRIER); + } + } + break; + } case DICTIONARY_ELEMENTS: { + AssertNoAllocation no_gc; + WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc); NumberDictionary* dictionary = NumberDictionary::cast(elements()); for (int i = 0; i < dictionary->Capacity(); i++) { Object* key = dictionary->KeyAt(i); @@ -7137,6 +7195,55 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, } +MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( + int capacity, + int length) { + Heap* heap = GetHeap(); + // We should never end in here with a pixel or external array. + ASSERT(!HasExternalArrayElements()); + + Object* obj; + { MaybeObject* maybe_obj = + heap->AllocateUninitializedFixedDoubleArray(capacity); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + FixedDoubleArray* elems = FixedDoubleArray::cast(obj); + + { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + Map* new_map = Map::cast(obj); + + AssertNoAllocation no_gc; + switch (GetElementsKind()) { + case FAST_ELEMENTS: { + elems->Initialize(FixedArray::cast(elements())); + break; + } + case FAST_DOUBLE_ELEMENTS: { + elems->Initialize(FixedDoubleArray::cast(elements())); + break; + } + case DICTIONARY_ELEMENTS: { + elems->Initialize(NumberDictionary::cast(elements())); + break; + } + default: + UNREACHABLE(); + break; + } + + set_map(new_map); + set_elements(elems); + + if (IsJSArray()) { + JSArray::cast(this)->set_length(Smi::FromInt(length)); + } + + return this; +} + + MaybeObject* JSObject::SetSlowElements(Object* len) { // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); @@ -7858,7 +7965,6 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, if (found) return result; } - // Check whether there is extra space in fixed array.. if (index < elms_length) { elms->set(index, value); @@ -7900,6 +8006,89 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, } +MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( + uint32_t index, + Object* value, + StrictModeFlag strict_mode, + bool check_prototype) { + ASSERT(HasFastDoubleElements()); + + FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); + uint32_t elms_length = static_cast(elms->length()); + + // If storing to an element that isn't in the array, pass the store request + // up the prototype chain before storing in the receiver's elements. + if (check_prototype && + (index >= elms_length || elms->is_the_hole(index))) { + bool found; + MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, + value, + &found, + strict_mode); + if (found) return result; + } + + // If the value object is not a heap number, switch to fast elements and try + // again. + bool value_is_smi = value->IsSmi(); + if (!value->IsNumber()) { + Object* obj; + uint32_t length = elms_length; + if (IsJSArray()) { + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); + } + MaybeObject* maybe_obj = + SetFastElementsCapacityAndLength(elms_length, length); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + return SetFastElement(index, value, strict_mode, check_prototype); + } + + double double_value = value_is_smi + ? static_cast(Smi::cast(value)->value()) + : HeapNumber::cast(value)->value(); + + // Check whether there is extra space in the fixed array. + if (index < elms_length) { + elms->set(index, double_value); + if (IsJSArray()) { + // Update the length of the array if needed. + uint32_t array_length = 0; + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); + if (index >= array_length) { + JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); + } + } + return value; + } + + // Allow gap in fast case. + if ((index - elms_length) < kMaxGap) { + // Try allocating extra space. + int new_capacity = NewElementsCapacity(index+1); + if (new_capacity <= kMaxFastElementsLength || + !ShouldConvertToSlowElements(new_capacity)) { + ASSERT(static_cast(new_capacity) > index); + Object* obj; + { MaybeObject* maybe_obj = + SetFastDoubleElementsCapacityAndLength(new_capacity, + index + 1); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + FixedDoubleArray::cast(elements())->set(index, double_value); + return value; + } + } + + // Otherwise default to slow case. + Object* obj; + { MaybeObject* maybe_obj = NormalizeElements(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + ASSERT(HasDictionaryElements()); + return SetElement(index, value, strict_mode, check_prototype); +} + + MaybeObject* JSObject::SetElement(uint32_t index, Object* value, StrictModeFlag strict_mode, @@ -7949,6 +8138,8 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, case FAST_ELEMENTS: // Fast case. return SetFastElement(index, value, strict_mode, check_prototype); + case FAST_DOUBLE_ELEMENTS: + return SetFastDoubleElement(index, value, strict_mode, check_prototype); case EXTERNAL_PIXEL_ELEMENTS: { ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); return pixels->SetValue(index, value); @@ -8072,24 +8263,35 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, } else { new_length = NumberDictionary::cast(elements())->max_number_key() + 1; } - Object* obj; - { MaybeObject* maybe_obj = - SetFastElementsCapacityAndLength(new_length, new_length); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + if (ShouldConvertToFastDoubleElements()) { + Object* obj; + { MaybeObject* maybe_obj = + SetFastDoubleElementsCapacityAndLength(new_length, new_length); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } #ifdef DEBUG - if (FLAG_trace_normalization) { - PrintF("Object elements are fast case again:\n"); - Print(); - } + if (FLAG_trace_normalization) { + PrintF("Object elements are fast double case again:\n"); + Print(); + } #endif + } else { + Object* obj; + { MaybeObject* maybe_obj = + SetFastElementsCapacityAndLength(new_length, new_length); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } +#ifdef DEBUG + if (FLAG_trace_normalization) { + PrintF("Object elements are fast case again:\n"); + Print(); + } +#endif + } } return value; } - default: - UNREACHABLE(); - break; } // All possible cases have been handled above. Add a return to avoid the // complaints from the compiler. @@ -8129,6 +8331,15 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver, } break; } + case FAST_DOUBLE_ELEMENTS: { + FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); + if (index < static_cast(elms->length())) { + if (!elms->is_the_hole(index)) { + return GetHeap()->NumberFromDouble(elms->get(index)); + } + } + break; + } case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: @@ -8232,6 +8443,16 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver, } break; } + case FAST_DOUBLE_ELEMENTS: { + FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); + if (index < static_cast(elms->length())) { + if (!elms->is_the_hole(index)) { + double double_value = elms->get(index); + return GetHeap()->NumberFromDouble(double_value); + } + } + break; + } case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: @@ -8351,6 +8572,7 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) { } break; } + case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: UNREACHABLE(); @@ -8373,6 +8595,14 @@ bool JSObject::HasDenseElements() { } break; } + case FAST_DOUBLE_ELEMENTS: { + FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); + capacity = elms->length(); + for (int i = 0; i < capacity; i++) { + if (!elms->is_the_hole(i)) number_of_elements++; + } + break; + } case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: @@ -8401,11 +8631,17 @@ bool JSObject::HasDenseElements() { bool JSObject::ShouldConvertToSlowElements(int new_capacity) { - ASSERT(HasFastElements()); // Keep the array in fast case if the current backing storage is // almost filled and if the new capacity is no more than twice the // old capacity. - int elements_length = FixedArray::cast(elements())->length(); + int elements_length = 0; + if (HasFastElements()) { + elements_length = FixedArray::cast(elements())->length(); + } else if (HasFastDoubleElements()) { + elements_length = FixedDoubleArray::cast(elements())->length(); + } else { + UNREACHABLE(); + } return !HasDenseElements() || ((new_capacity / 2) > elements_length); } @@ -8435,6 +8671,23 @@ bool JSObject::ShouldConvertToFastElements() { } +bool JSObject::ShouldConvertToFastDoubleElements() { + if (FLAG_unbox_double_arrays) { + ASSERT(HasDictionaryElements()); + NumberDictionary* dictionary = NumberDictionary::cast(elements()); + for (int i = 0; i < dictionary->Capacity(); i++) { + Object* key = dictionary->KeyAt(i); + if (key->IsNumber()) { + if (!dictionary->ValueAt(i)->IsNumber()) return false; + } + } + return true; + } else { + return false; + } +} + + // Certain compilers request function template instantiation when they // see the definition of the other template functions in the // class. This requires us to have the template functions put diff --git a/src/objects.h b/src/objects.h index 118e71b..5583abd 100644 --- a/src/objects.h +++ b/src/objects.h @@ -533,6 +533,7 @@ enum InstanceType { EXTERNAL_FLOAT_ARRAY_TYPE, EXTERNAL_DOUBLE_ARRAY_TYPE, EXTERNAL_PIXEL_ARRAY_TYPE, // LAST_EXTERNAL_ARRAY_TYPE + FIXED_DOUBLE_ARRAY_TYPE, FILLER_TYPE, // LAST_DATA_TYPE // Structs. @@ -732,6 +733,7 @@ class MaybeObject BASE_EMBEDDED { V(DeoptimizationInputData) \ V(DeoptimizationOutputData) \ V(FixedArray) \ + V(FixedDoubleArray) \ V(Context) \ V(CatchContext) \ V(GlobalContext) \ @@ -800,6 +802,10 @@ class Object : public MaybeObject { // Extract the number. inline double Number(); + // Returns true if the object is of the correct type to be used as a + // implementation of a JSObject's elements. + inline bool HasValidElements(); + inline bool HasSpecificClassOf(String* name); MUST_USE_RESULT MaybeObject* ToObject(); // ECMA-262 9.9. @@ -1427,6 +1433,10 @@ class JSObject: public JSReceiver { // The "fast" kind for tagged values. Must be first to make it possible // to efficiently check maps if they have fast elements. FAST_ELEMENTS, + + // The "fast" kind for unwrapped, non-tagged double values. + FAST_DOUBLE_ELEMENTS, + // The "slow" kind. DICTIONARY_ELEMENTS, // The "fast" kind for external arrays @@ -1478,6 +1488,7 @@ class JSObject: public JSReceiver { MUST_USE_RESULT inline MaybeObject* ResetElements(); inline ElementsKind GetElementsKind(); inline bool HasFastElements(); + inline bool HasFastDoubleElements(); inline bool HasDictionaryElements(); inline bool HasExternalPixelElements(); inline bool HasExternalArrayElements(); @@ -1637,6 +1648,9 @@ class JSObject: public JSReceiver { // storage would. In that case the JSObject should have fast // elements. bool ShouldConvertToFastElements(); + // Returns true if the elements of JSObject contains only values that can be + // represented in a FixedDoubleArray. + bool ShouldConvertToFastDoubleElements(); // Tells whether the index'th element is present. inline bool HasElement(uint32_t index); @@ -1676,6 +1690,12 @@ class JSObject: public JSReceiver { StrictModeFlag strict_mode, bool check_prototype = true); + MUST_USE_RESULT MaybeObject* SetFastDoubleElement( + uint32_t index, + Object* value, + StrictModeFlag strict_mode, + bool check_prototype = true); + // Set the index'th array element. // A Failure object is returned if GC is needed. MUST_USE_RESULT MaybeObject* SetElement(uint32_t index, @@ -1695,6 +1715,9 @@ class JSObject: public JSReceiver { MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity, int length); + MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength( + int capacity, + int length); MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length); // Lookup interceptors are used for handling properties controlled by host @@ -1986,13 +2009,26 @@ class JSObject: public JSReceiver { }; -// FixedArray describes fixed-sized arrays with element type Object*. -class FixedArray: public HeapObject { +// Common superclass for FixedArrays that allow implementations to share +// common accessors and some code paths. +class FixedArrayBase: public HeapObject { public: // [length]: length of the array. inline int length(); inline void set_length(int value); + inline static FixedArrayBase* cast(Object* object); + + // Layout description. + // Length is smi tagged when it is stored. + static const int kLengthOffset = HeapObject::kHeaderSize; + static const int kHeaderSize = kLengthOffset + kPointerSize; +}; + + +// FixedArray describes fixed-sized arrays with element type Object*. +class FixedArray: public FixedArrayBase { + public: // Setter and getter for elements. inline Object* get(int index); // Setter that uses write barrier. @@ -2043,11 +2079,6 @@ class FixedArray: public HeapObject { // Casting. static inline FixedArray* cast(Object* obj); - // Layout description. - // Length is smi tagged when it is stored. - static const int kLengthOffset = HeapObject::kHeaderSize; - static const int kHeaderSize = kLengthOffset + kPointerSize; - // Maximal allowed size, in bytes, of a single FixedArray. // Prevents overflowing size computations, as well as extreme memory // consumption. @@ -2095,6 +2126,71 @@ class FixedArray: public HeapObject { }; +// FixedDoubleArray describes fixed-sized arrays with element type double. +class FixedDoubleArray: public FixedArrayBase { + public: + inline void Initialize(FixedArray* from); + inline void Initialize(FixedDoubleArray* from); + inline void Initialize(NumberDictionary* from); + + // Setter and getter for elements. + inline double get(int index); + inline void set(int index, double value); + inline void set_the_hole(int index); + + // Checking for the hole. + inline bool is_the_hole(int index); + + // Garbage collection support. + inline static int SizeFor(int length) { + return kHeaderSize + length * kDoubleSize; + } + + // The following can't be declared inline as const static + // because they're 64-bit. + static uint64_t kCanonicalNonHoleNanLower32; + static uint64_t kCanonicalNonHoleNanInt64; + static uint64_t kHoleNanInt64; + + inline static bool is_the_hole_nan(double value) { + return BitCast(value) == kHoleNanInt64; + } + + inline static double hole_nan_as_double() { + return BitCast(kHoleNanInt64); + } + + inline static double canonical_not_the_hole_nan_as_double() { + return BitCast(kCanonicalNonHoleNanInt64); + } + + // Casting. + static inline FixedDoubleArray* cast(Object* obj); + + // Maximal allowed size, in bytes, of a single FixedDoubleArray. + // Prevents overflowing size computations, as well as extreme memory + // consumption. + static const int kMaxSize = 512 * MB; + // Maximally allowed length of a FixedArray. + static const int kMaxLength = (kMaxSize - kHeaderSize) / kDoubleSize; + + // Dispatched behavior. +#ifdef OBJECT_PRINT + inline void FixedDoubleArrayPrint() { + FixedDoubleArrayPrint(stdout); + } + void FixedDoubleArrayPrint(FILE* out); +#endif + +#ifdef DEBUG + void FixedDoubleArrayVerify(); +#endif + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(FixedDoubleArray); +}; + + // DescriptorArrays are fixed arrays used to hold instance descriptors. // The format of the these objects is: // TODO(1399): It should be possible to make room for bit_field3 in the map @@ -3809,6 +3905,10 @@ class Map: public HeapObject { return elements_kind() == JSObject::FAST_ELEMENTS; } + inline bool has_fast_double_elements() { + return elements_kind() == JSObject::FAST_DOUBLE_ELEMENTS; + } + inline bool has_external_array_elements() { JSObject::ElementsKind kind(elements_kind()); return kind >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && @@ -3901,18 +4001,23 @@ class Map: public HeapObject { // instance descriptors. MUST_USE_RESULT MaybeObject* CopyDropTransitions(); - // Returns this map if it has the fast elements bit set, otherwise + // Returns this map if it already has elements that are fast, otherwise // returns a copy of the map, with all transitions dropped from the - // descriptors and the fast elements bit set. + // descriptors and the ElementsKind set to FAST_ELEMENTS. MUST_USE_RESULT inline MaybeObject* GetFastElementsMap(); - // Returns this map if it has the fast elements bit cleared, - // otherwise returns a copy of the map, with all transitions dropped - // from the descriptors and the fast elements bit cleared. + // Returns this map if it already has fast elements that are doubles, + // otherwise returns a copy of the map, with all transitions dropped from the + // descriptors and the ElementsKind set to FAST_DOUBLE_ELEMENTS. + MUST_USE_RESULT inline MaybeObject* GetFastDoubleElementsMap(); + + // Returns this map if already has dictionary elements, otherwise returns a + // copy of the map, with all transitions dropped from the descriptors and the + // ElementsKind set to DICTIONARY_ELEMENTS. MUST_USE_RESULT inline MaybeObject* GetSlowElementsMap(); // Returns a new map with all transitions dropped from the descriptors and the - // external array elements bit set. + // ElementsKind set to one of the value corresponding to array_type. MUST_USE_RESULT MaybeObject* GetExternalArrayElementsMap( ExternalArrayType array_type, bool safe_to_add_transition); diff --git a/src/spaces.cc b/src/spaces.cc index b494d24..23c87cd 100644 --- a/src/spaces.cc +++ b/src/spaces.cc @@ -3070,7 +3070,7 @@ void LargeObjectSpace::Verify() { // strings), fixed arrays, and byte arrays in large object space. ASSERT(object->IsCode() || object->IsSeqString() || object->IsExternalString() || object->IsFixedArray() || - object->IsByteArray()); + object->IsFixedDoubleArray() || object->IsByteArray()); // The object itself should look OK. object->Verify(); diff --git a/src/v8-counters.h b/src/v8-counters.h index e3b16e9..17e6701 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -126,8 +126,9 @@ namespace internal { V8.GCCompactorCausedByWeakHandles) \ SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \ SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \ - SC(map_slow_to_fast_elements, V8.MapSlowToFastElements) \ - SC(map_fast_to_slow_elements, V8.MapFastToSlowElements) \ + SC(map_to_fast_elements, V8.MapToFastElements) \ + SC(map_to_fast_double_elements, V8.MapToFastDoubleElements) \ + SC(map_to_slow_elements, V8.MapToSlowElements) \ SC(map_to_external_array_elements, V8.MapToExternalArrayElements) \ /* How is the generic keyed-load stub used? */ \ SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ diff --git a/test/mjsunit/unbox-double-arrays.js b/test/mjsunit/unbox-double-arrays.js new file mode 100644 index 0000000..31918b3 --- /dev/null +++ b/test/mjsunit/unbox-double-arrays.js @@ -0,0 +1,78 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test dictionary -> double elements -> dictionary elements round trip + +var foo = new Array(500000); + +function func(a) { + for (var i= 0; i < 100000; ++i ) { + a[i] = i+0.5; + } +} + +func(foo); + +for (var i= 0; i < 100000; i += 500 ) { + assertEquals(i+0.5, foo[i]); +} + +delete foo[5]; +// Don't use assertEquals for comparison to undefined due to +assertTrue(undefined === foo[5]); +assertTrue(undefined === foo[500000-1]); +assertTrue(undefined === foo[-1]); +assertEquals(500000, foo.length); + +// Cause the array to grow beyond it's JSArray length. This will double the +// size of the capacity and force the array into "slow" dictionary case. +foo[500001] = 50; +assertEquals(50, foo[500001]); +assertEquals(500002, foo.length); +assertTrue(undefined === foo[5]) +assertTrue(undefined === foo[500000-1]) +assertTrue(undefined === foo[-1]) +assertEquals(500002, foo.length); + +// Test dictionary -> double elements -> fast elements. + +var foo2 = new Array(500000); +func(foo2); +delete foo2[5]; + +// Convert back to fast elements and make sure the contents of the array are +// unchanged. +foo2[25] = new Object(); +for (var i= 0; i < 100000; i += 500 ) { + if (i != 25 && i != 5) { + assertEquals(i+0.5, foo2[i]); + } +} +assertTrue(undefined === foo2[5]) +assertTrue(undefined === foo2[500000-1]) +assertTrue(undefined === foo2[-1]) +assertEquals(500000, foo2.length); -- 2.7.4