From: danno@chromium.org Date: Thu, 22 Sep 2011 11:30:04 +0000 (+0000) Subject: Basic support for tracking smi-only arrays on ia32. X-Git-Tag: upstream/4.7.83~18398 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f48c9f65572cd7f600adb05fe6166b8838134586;p=platform%2Fupstream%2Fv8.git Basic support for tracking smi-only arrays on ia32. Activated by the flag --smi-only-arrays Currently not crankshaft support, using flag on non-ia32 platforms will lead to write barrier misses and crashes. BUG=none TEST=elements_kind.js Review URL: http://codereview.chromium.org/7901016 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9392 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 990a093..1e98f67 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2615,6 +2615,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3457,6 +3458,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 7f5a81c..a77fd92 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -1859,7 +1859,8 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); b(hi, fail); diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 2f004d8..b005f59 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -3493,6 +3493,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3579,6 +3580,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3919,6 +3921,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3982,6 +3985,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -4121,6 +4125,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -4273,8 +4278,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : key @@ -4324,6 +4331,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); __ str(value_reg, MemOperand(scratch)); __ mov(receiver_reg, value_reg); + ASSERT(elements_kind == FAST_ELEMENTS); __ RecordWrite(elements_reg, // Object. scratch, // Address. receiver_reg, // Value. diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 6fde0b6..25467d1 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1076,6 +1076,11 @@ void Genesis::InitializeGlobal(Handle inner_global, elements->set(0, *array); array = factory->NewFixedArray(0); elements->set(1, *array); + Handle non_strict_arguments_elements_map = + factory->GetElementsTransitionMap(result, + NON_STRICT_ARGUMENTS_ELEMENTS); + result->set_map(*non_strict_arguments_elements_map); + ASSERT(result->HasNonStrictArgumentsElements()); result->set_elements(*elements); global_context()->set_aliased_arguments_boilerplate(*result); } @@ -1557,6 +1562,18 @@ bool Genesis::InstallNatives() { isolate()->builtins()->builtin(Builtins::kArrayConstructCode)); array_function->shared()->DontAdaptArguments(); + // InternalArrays should not use Smi-Only array optimizations. There are too + // many places in the C++ runtime code (e.g. RegEx) that assume that + // elements in InternalArrays can be set to non-Smi values without going + // through a common bottleneck that would make the SMI_ONLY -> FAST_ELEMENT + // transition easy to trap. Moreover, they rarely are smi-only. + MaybeObject* maybe_map = + array_function->initial_map()->CopyDropTransitions(); + Map* new_map; + if (!maybe_map->To(&new_map)) return maybe_map; + new_map->set_elements_kind(FAST_ELEMENTS); + array_function->set_initial_map(new_map); + // Make "length" magic on instances. Handle array_descriptors = factory()->CopyAppendForeignDescriptor( diff --git a/src/builtins.cc b/src/builtins.cc index 4af5b3a..591904e 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -203,7 +203,7 @@ BUILTIN(ArrayCodeGeneric) { } // 'array' now contains the JSArray we should initialize. - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); // Optimize the case where there is one argument and the argument is a // small smi. @@ -216,7 +216,8 @@ BUILTIN(ArrayCodeGeneric) { { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - array->SetContent(FixedArray::cast(obj)); + MaybeObject* maybe_obj = array->SetContent(FixedArray::cast(obj)); + if (maybe_obj->IsFailure()) return maybe_obj; return array; } } @@ -240,6 +241,13 @@ BUILTIN(ArrayCodeGeneric) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } + // Set length and elements on the array. + if (FLAG_smi_only_arrays) { + MaybeObject* maybe_object = + array->EnsureCanContainElements(FixedArray::cast(obj)); + if (maybe_object->IsFailure()) return maybe_object; + } + AssertNoAllocation no_gc; FixedArray* elms = FixedArray::cast(obj); WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); @@ -248,7 +256,6 @@ BUILTIN(ArrayCodeGeneric) { elms->set(index, args[index+1], mode); } - // Set length and elements on the array. array->set_elements(FixedArray::cast(obj)); array->set_length(len); @@ -486,9 +493,11 @@ BUILTIN(ArrayPush) { FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; - array->set_elements(elms); } + MaybeObject* maybe = array->EnsureCanContainElements(&args, 1, to_add); + if (maybe->IsFailure()) return maybe; + // Add the provided values. AssertNoAllocation no_gc; WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); @@ -496,6 +505,10 @@ BUILTIN(ArrayPush) { elms->set(index + len, args[index + 1], mode); } + if (elms != array->elements()) { + array->set_elements(elms); + } + // Set the length. array->set_length(Smi::FromInt(new_length)); return Smi::FromInt(new_length); @@ -550,7 +563,7 @@ BUILTIN(ArrayShift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); if (len == 0) return heap->undefined_value(); @@ -592,7 +605,7 @@ BUILTIN(ArrayUnshift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); int to_add = args.length() - 1; @@ -601,6 +614,12 @@ BUILTIN(ArrayUnshift) { // we should never hit this case. ASSERT(to_add <= (Smi::kMaxValue - len)); + if (FLAG_smi_only_arrays) { + MaybeObject* maybe_object = + array->EnsureCanContainElements(&args, 1, to_add); + if (maybe_object->IsFailure()) return maybe_object; + } + if (new_length > elms->length()) { // New backing storage is needed. int capacity = new_length + (new_length >> 1) + 16; @@ -609,13 +628,11 @@ BUILTIN(ArrayUnshift) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* new_elms = FixedArray::cast(obj); - AssertNoAllocation no_gc; if (len > 0) { CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len); } FillWithHoles(heap, new_elms, new_length, capacity); - elms = new_elms; array->set_elements(elms); } else { @@ -730,6 +747,12 @@ BUILTIN(ArraySlice) { } FixedArray* result_elms = FixedArray::cast(result); + if (FLAG_smi_only_arrays) { + MaybeObject* maybe_object = + result_array->EnsureCanContainElements(result_elms); + if (maybe_object->IsFailure()) return maybe_object; + } + AssertNoAllocation no_gc; CopyElements(heap, &no_gc, result_elms, 0, elms, k, result_len); @@ -757,7 +780,7 @@ BUILTIN(ArraySplice) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); @@ -835,8 +858,14 @@ BUILTIN(ArraySplice) { int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0; + if (FLAG_smi_only_arrays) { + MaybeObject* maybe = array->EnsureCanContainElements(&args, 3, item_count); + if (maybe->IsFailure()) return maybe; + } + int new_length = len - actual_delete_count + item_count; + bool elms_changed = false; if (item_count < actual_delete_count) { // Shrink the array. const bool trim_array = !heap->lo_space()->Contains(elms) && @@ -851,7 +880,8 @@ BUILTIN(ArraySplice) { } elms = LeftTrimFixedArray(heap, elms, delta); - array->set_elements(elms); + + elms_changed = true; } else { AssertNoAllocation no_gc; MoveElements(heap, &no_gc, @@ -891,7 +921,7 @@ BUILTIN(ArraySplice) { FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; - array->set_elements(elms); + elms_changed = true; } else { AssertNoAllocation no_gc; MoveElements(heap, &no_gc, @@ -907,6 +937,10 @@ BUILTIN(ArraySplice) { elms->set(k, args[3 + k - actual_start], mode); } + if (elms_changed) { + array->set_elements(elms); + } + // Set the length. array->set_length(Smi::FromInt(new_length)); @@ -965,6 +999,19 @@ BUILTIN(ArrayConcat) { } FixedArray* result_elms = FixedArray::cast(result); + if (FLAG_smi_only_arrays) { + for (int i = 0; i < n_arguments; i++) { + JSArray* array = JSArray::cast(args[i]); + int len = Smi::cast(array->length())->value(); + if (len > 0) { + FixedArray* elms = FixedArray::cast(array->elements()); + MaybeObject* maybe_object = + result_array->EnsureCanContainElements(elms); + if (maybe_object->IsFailure()) return maybe_object; + } + } + } + // Copy data. AssertNoAllocation no_gc; int start_pos = 0; diff --git a/src/code-stubs.cc b/src/code-stubs.cc index f309a82..6670796 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -250,6 +250,7 @@ void InstanceofStub::PrintName(StringStream* stream) { void KeyedLoadElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastElement(masm); break; case FAST_DOUBLE_ELEMENTS: @@ -279,7 +280,11 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) { void KeyedStoreElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: - KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_); + case FAST_SMI_ONLY_ELEMENTS: { + KeyedStoreStubCompiler::GenerateStoreFastElement(masm, + is_js_array_, + elements_kind_); + } break; case FAST_DOUBLE_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, diff --git a/src/elements.cc b/src/elements.cc index e4ecfe8..5e7a84e 100644 --- a/src/elements.cc +++ b/src/elements.cc @@ -227,7 +227,9 @@ class FastElementsAccessor public: static MaybeObject* DeleteCommon(JSObject* obj, uint32_t key) { - ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements()); + ASSERT(obj->HasFastElements() || + obj->HasFastSmiOnlyElements() || + obj->HasFastArgumentsElements()); Heap* heap = obj->GetHeap(); FixedArray* backing_store = FixedArray::cast(obj->elements()); if (backing_store->map() == heap->non_strict_arguments_elements_map()) { @@ -596,6 +598,9 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { void ElementsAccessor::InitializeOncePerProcess() { static struct ConcreteElementsAccessors { + // Use the fast element handler for smi-only arrays. The implementation is + // currently identical. + FastElementsAccessor fast_smi_elements_handler; FastElementsAccessor fast_elements_handler; FastDoubleElementsAccessor fast_double_elements_handler; DictionaryElementsAccessor dictionary_elements_handler; @@ -612,6 +617,7 @@ void ElementsAccessor::InitializeOncePerProcess() { } element_accessors; static ElementsAccessor* accessor_array[] = { + &element_accessors.fast_smi_elements_handler, &element_accessors.fast_elements_handler, &element_accessors.fast_double_elements_handler, &element_accessors.dictionary_elements_handler, @@ -627,6 +633,9 @@ void ElementsAccessor::InitializeOncePerProcess() { &element_accessors.pixel_elements_handler }; + STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) == + kElementsKindCount); + elements_accessors_ = accessor_array; } diff --git a/src/factory.cc b/src/factory.cc index 9ed3c44..a40dbd7 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -404,10 +404,12 @@ Handle Factory::NewJSGlobalPropertyCell( } -Handle Factory::NewMap(InstanceType type, int instance_size) { +Handle Factory::NewMap(InstanceType type, + int instance_size, + ElementsKind elements_kind) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->AllocateMap(type, instance_size), + isolate()->heap()->AllocateMap(type, instance_size, elements_kind), Map); } @@ -710,7 +712,12 @@ Handle Factory::NewFunctionWithPrototype(Handle name, if (force_initial_map || type != JS_OBJECT_TYPE || instance_size != JSObject::kHeaderSize) { - Handle initial_map = NewMap(type, instance_size); + ElementsKind default_elements_kind = FLAG_smi_only_arrays + ? FAST_SMI_ONLY_ELEMENTS + : FAST_ELEMENTS; + Handle initial_map = NewMap(type, + instance_size, + default_elements_kind); function->set_initial_map(*initial_map); initial_map->set_constructor(*function); } @@ -896,11 +903,26 @@ Handle Factory::NewJSArrayWithElements(Handle elements, Handle result = Handle::cast(NewJSObject(isolate()->array_function(), pretenure)); - result->SetContent(*elements); + SetContent(result, elements); return result; } +void Factory::SetContent(Handle array, + Handle elements) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + array->SetContent(*elements)); +} + + +void Factory::EnsureCanContainNonSmiElements(Handle array) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + array->EnsureCanContainNonSmiElements()); +} + + Handle Factory::NewJSProxy(Handle handler, Handle prototype) { CALL_HEAP_FUNCTION( diff --git a/src/factory.h b/src/factory.h index 422f7e7..819c1a6 100644 --- a/src/factory.h +++ b/src/factory.h @@ -203,7 +203,9 @@ class Factory { Handle NewJSGlobalPropertyCell( Handle value); - Handle NewMap(InstanceType type, int instance_size); + Handle NewMap(InstanceType type, + int instance_size, + ElementsKind elements_kind = FAST_ELEMENTS); Handle NewFunctionPrototype(Handle function); @@ -253,6 +255,10 @@ class Factory { Handle elements, PretenureFlag pretenure = NOT_TENURED); + void SetContent(Handle array, Handle elements); + + void EnsureCanContainNonSmiElements(Handle array); + Handle NewJSProxy(Handle handler, Handle prototype); // Change the type of the argument into a JS object/function and reinitialize. diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 161dba4..358d67e 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -104,6 +104,7 @@ DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping") // Flags for experimental implementation features. DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles") +DEFINE_bool(smi_only_arrays, false, "tracks arrays with only smi values") DEFINE_bool(string_slices, false, "use string slices") // Flags for Crankshaft. diff --git a/src/heap.cc b/src/heap.cc index 45aa577..9000c2d 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1700,7 +1700,9 @@ MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type, } -MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) { +MaybeObject* Heap::AllocateMap(InstanceType instance_type, + int instance_size, + ElementsKind elements_kind) { Object* result; { MaybeObject* maybe_result = AllocateRawMap(); if (!maybe_result->ToObject(&result)) return maybe_result; @@ -1722,7 +1724,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) { map->set_unused_property_fields(0); map->set_bit_field(0); map->set_bit_field2(1 << Map::kIsExtensible); - map->set_elements_kind(FAST_ELEMENTS); + map->set_elements_kind(elements_kind); // If the map object is aligned fill the padding area with Smi 0 objects. if (Map::kPadStart < Map::kSize) { @@ -2112,7 +2114,13 @@ bool Heap::CreateApiObjects() { { MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); if (!maybe_obj->ToObject(&obj)) return false; } - set_neander_map(Map::cast(obj)); + // Don't use Smi-only elements optimizations for objects with the neander + // map. There are too many cases where element values are set directly with a + // bottleneck to trap the Smi-only -> fast elements transition, and there + // appears to be no benefit for optimize this case. + Map* new_neander_map = Map::cast(obj); + new_neander_map->set_elements_kind(FAST_ELEMENTS); + set_neander_map(new_neander_map); { MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map()); if (!maybe_obj->ToObject(&obj)) return false; @@ -3503,7 +3511,8 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { InitializeJSObjectFromMap(JSObject::cast(obj), FixedArray::cast(properties), map); - ASSERT(JSObject::cast(obj)->HasFastElements()); + ASSERT(JSObject::cast(obj)->HasFastSmiOnlyElements() || + JSObject::cast(obj)->HasFastElements()); return obj; } @@ -3685,6 +3694,7 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { object_size); } + ASSERT(JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind()); FixedArrayBase* elements = FixedArrayBase::cast(source->elements()); FixedArray* properties = FixedArray::cast(source->properties()); // Update elements if necessary. diff --git a/src/heap.h b/src/heap.h index d89506e..7f05b4e 100644 --- a/src/heap.h +++ b/src/heap.h @@ -523,8 +523,10 @@ class Heap { // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. // Please note this function does not perform a garbage collection. - MUST_USE_RESULT MaybeObject* AllocateMap(InstanceType instance_type, - int instance_size); + MUST_USE_RESULT MaybeObject* AllocateMap( + InstanceType instance_type, + int instance_size, + ElementsKind elements_kind = FAST_ELEMENTS); // Allocates a partial map for bootstrapping. MUST_USE_RESULT MaybeObject* AllocatePartialMap(InstanceType instance_type, diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 5630ce3..658f5e1 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1488,6 +1488,7 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo( stream->Add("pixel"); break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -1582,6 +1583,7 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( case EXTERNAL_PIXEL_ELEMENTS: stream->Add("pixel"); break; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 4d6b53b..f6588bc 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -3336,7 +3336,12 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { HValue* key = AddInstruction( new(zone()) HConstant(Handle(Smi::FromInt(i)), Representation::Integer32())); - AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); + if (FLAG_smi_only_arrays) { + AddInstruction(BuildStoreKeyedGeneric(literal, key, value)); + } else { + AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); + } + AddSimulate(expr->GetIdForElement(i)); } return ast_context()->ReturnValue(Pop()); @@ -3947,6 +3952,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: break; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -4058,14 +4064,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HLoadExternalArrayPointer* external_elements = NULL; HInstruction* checked_key = NULL; - // FAST_ELEMENTS is assumed to be the first case. - STATIC_ASSERT(FAST_ELEMENTS == 0); + // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external + // arrays. + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); - for (ElementsKind elements_kind = FAST_ELEMENTS; + for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND; elements_kind <= LAST_ELEMENTS_KIND; elements_kind = ElementsKind(elements_kind + 1)) { - // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we - // need to add some code that's executed for all external array cases. + // After having handled FAST_ELEMENTS, FAST_SMI_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code + // that's executed for all external array cases. STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == LAST_ELEMENTS_KIND); if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND @@ -4087,11 +4099,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_true); HInstruction* access; - if (elements_kind == FAST_ELEMENTS || + if (elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_ELEMENTS || elements_kind == FAST_DOUBLE_ELEMENTS) { bool fast_double_elements = elements_kind == FAST_DOUBLE_ELEMENTS; - if (is_store && elements_kind == FAST_ELEMENTS) { + if (is_store && !fast_double_elements) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map(), elements_kind_branch)); @@ -4105,29 +4118,40 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, current_block()->Finish(typecheck); set_current_block(if_jsarray); - HInstruction* length = new(zone()) HJSArrayLength(object, typecheck); - AddInstruction(length); - checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val)); - } else { - access = AddInstruction( - new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); - } + HInstruction* length; + if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) { + // For now, fall back to the generic stub for + // FAST_SMI_ONLY_ELEMENTS + access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); } else { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); + length = new(zone()) HJSArrayLength(object, typecheck); + AddInstruction(length); + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); + if (is_store) { + if (fast_double_elements) { + access = AddInstruction( + new(zone()) HStoreKeyedFastDoubleElement(elements, + checked_key, + val)); + } else { + access = AddInstruction( + new(zone()) HStoreKeyedFastElement(elements, + checked_key, + val)); + } } else { - access = AddInstruction( - new(zone()) HLoadKeyedFastElement(elements, checked_key)); + if (fast_double_elements) { + access = AddInstruction( + new(zone()) HLoadKeyedFastDoubleElement(elements, + checked_key)); + } else { + access = AddInstruction( + new(zone()) HLoadKeyedFastElement(elements, checked_key)); + } + Push(access); } - Push(access); } + *has_side_effects |= access->HasSideEffects(); if (position != -1) { access->set_position(position); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 738d23f..0c7dd2d 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1492,8 +1492,20 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ mov(FieldOperand(ebx, offset), result_register()); + Label no_map_change; + __ JumpIfSmi(result_register(), &no_map_change); // Update the write barrier for the array store. - __ RecordWriteField(ebx, offset, result_register(), ecx, kDontSaveFPRegs); + __ RecordWriteField(ebx, offset, result_register(), ecx, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + if (FLAG_smi_only_arrays) { + __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset)); + __ CheckFastSmiOnlyElements(edi, &no_map_change, Label::kNear); + __ push(Operand(esp, 0)); + __ CallRuntime(Runtime::kNonSmiElementStored, 1); + } + __ bind(&no_map_change); PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index e81357c..c2181e7 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -810,6 +810,19 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // ecx: key (a smi) // edx: receiver // edi: FixedArray receiver->elements + + if (FLAG_smi_only_arrays) { + Label not_smi_only; + // Make sure the elements are smi-only. + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + __ CheckFastSmiOnlyElements(ebx, ¬_smi_only, Label::kNear); + // Non-smis need to call into the runtime if the array is smi only. + __ JumpIfNotSmi(eax, &slow); + __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax); + __ ret(0); + __ bind(¬_smi_only); + } + __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax); // Update write barrier for the elements array address. diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 46d8050..fb67429 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2400,6 +2400,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -3171,6 +3172,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 519210c..405ae51 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -374,13 +374,38 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); cmpb(FieldOperand(map, Map::kBitField2Offset), Map::kMaximumBitField2FastElementValue); j(above, fail, distance); } +void MacroAssembler::CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastSmiOnlyElementValue); + j(below_equal, fail, distance); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastElementValue); + j(above, fail, distance); +} + + +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastSmiOnlyElementValue); + j(above, fail, distance); +} + + void MacroAssembler::CheckMap(Register obj, Handle map, Label* fail, diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index a59dee8..d097da8 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -312,6 +312,18 @@ class MacroAssembler: public Assembler { Label* fail, Label::Distance distance = Label::kFar); + // Check if a map for a JSObject indicates that the object can have both smi + // and HeapObject elements. Jump to the specified label if it does not. + void CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + + // Check if a map for a JSObject indicates that the object has fast smi only + // elements. Jump to the specified label if it does not. + void CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + // Check if the map of an object is equal to a specified map and branch to // label if not. Skip the smi check if not required (object is known to be a // heap object) diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index c3cec4b..b4eff5a 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1454,7 +1454,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ j(not_equal, &call_builtin); if (argc == 1) { // Otherwise fall through to call builtin. - Label exit, attempt_to_grow_elements, with_write_barrier; + Label attempt_to_grow_elements, with_write_barrier; // Get the array's length into eax and calculate new length. __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset)); @@ -1469,6 +1469,10 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ cmp(eax, Operand(ecx)); __ j(greater, &attempt_to_grow_elements); + // Check if value is a smi. + __ mov(ecx, Operand(esp, argc * kPointerSize)); + __ JumpIfNotSmi(ecx, &with_write_barrier); + // Save new length. __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); @@ -1476,17 +1480,26 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ lea(edx, FieldOperand(ebx, eax, times_half_pointer_size, FixedArray::kHeaderSize - argc * kPointerSize)); - __ mov(ecx, Operand(esp, argc * kPointerSize)); __ mov(Operand(edx, 0), ecx); - // Check if value is a smi. - __ JumpIfNotSmi(ecx, &with_write_barrier); - - __ bind(&exit); __ ret((argc + 1) * kPointerSize); __ bind(&with_write_barrier); + if (FLAG_smi_only_arrays) { + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); + __ CheckFastObjectElements(edi, &call_builtin); + } + + // Save new length. + __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); + + // Push the element. + __ lea(edx, FieldOperand(ebx, + eax, times_half_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize)); + __ mov(Operand(edx, 0), ecx); + __ RecordWrite( ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); @@ -1497,6 +1510,17 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ jmp(&call_builtin); } + __ mov(edi, Operand(esp, argc * kPointerSize)); + if (FLAG_smi_only_arrays) { + // Growing elements that are SMI-only requires special handling in case + // the new element is non-Smi. For now, delegate to the builtin. + Label no_fast_elements_check; + __ JumpIfSmi(edi, &no_fast_elements_check); + __ mov(esi, FieldOperand(edx, HeapObject::kMapOffset)); + __ CheckFastObjectElements(esi, &call_builtin, Label::kFar); + __ bind(&no_fast_elements_check); + } + // We could be lucky and the elements array could be at the top of // new-space. In this case we can just grow it in place by moving the // allocation pointer up. @@ -1522,10 +1546,9 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // We fit and could grow elements. __ mov(Operand::StaticVariable(new_space_allocation_top), ecx); - __ mov(ecx, Operand(esp, argc * kPointerSize)); // Push the argument... - __ mov(Operand(edx, 0), ecx); + __ mov(Operand(edx, 0), edi); // ... and fill the rest with holes. for (int i = 1; i < kAllocationDelta; i++) { __ mov(Operand(edx, i * kPointerSize), @@ -1537,7 +1560,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // tell the incremental marker to rescan the object that we just grew. We // don't need to worry about the holes because they are in old space and // already marked black. - __ RecordWrite(ebx, edx, ecx, kDontSaveFPRegs, OMIT_REMEMBERED_SET); + __ RecordWrite(ebx, edx, edi, kDontSaveFPRegs, OMIT_REMEMBERED_SET); // Restore receiver to edx as finish sequence assumes it's here. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); @@ -3877,8 +3900,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key @@ -3909,12 +3934,28 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ j(above_equal, &miss_force_generic); } - // Do the store and update the write barrier. - __ lea(ecx, FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize)); - __ mov(Operand(ecx, 0), eax); - // Make sure to preserve the value in register eax. - __ mov(edx, Operand(eax)); - __ RecordWrite(edi, ecx, edx, kDontSaveFPRegs); + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + __ JumpIfNotSmi(eax, &miss_force_generic); + // ecx is a smi, don't use times_half_pointer_size istead of + // times_pointer_size + __ mov(FieldOperand(edi, + ecx, + times_half_pointer_size, + FixedArray::kHeaderSize), eax); + } else { + ASSERT(elements_kind == FAST_ELEMENTS); + // Do the store and update the write barrier. + // ecx is a smi, don't use times_half_pointer_size istead of + // times_pointer_size + __ lea(ecx, FieldOperand(edi, + ecx, + times_half_pointer_size, + FixedArray::kHeaderSize)); + __ mov(Operand(ecx, 0), eax); + // Make sure to preserve the value in register eax. + __ mov(edx, Operand(eax)); + __ RecordWrite(edi, ecx, edx, kDontSaveFPRegs); + } // Done. __ ret(0); diff --git a/src/ic.cc b/src/ic.cc index 84a62cd..1457d75 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1673,6 +1673,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck( } else { ASSERT(receiver_map->has_dictionary_elements() || receiver_map->has_fast_elements() || + receiver_map->has_fast_smi_only_elements() || receiver_map->has_fast_double_elements() || receiver_map->has_external_array_elements()); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; diff --git a/src/lithium.cc b/src/lithium.cc index d3d2238..31b1698 100644 --- a/src/lithium.cc +++ b/src/lithium.cc @@ -203,6 +203,7 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) { case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: return 3; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: diff --git a/src/liveobjectlist.cc b/src/liveobjectlist.cc index 54a2331..d62c4d1 100644 --- a/src/liveobjectlist.cc +++ b/src/liveobjectlist.cc @@ -1336,7 +1336,9 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, // Allocate the JSArray of the elements. Handle elements = factory->NewJSObject(isolate->array_function()); if (elements->IsFailure()) return Object::cast(*elements); - Handle::cast(elements)->SetContent(*elements_arr); + + maybe_result = Handle::cast(elements)->SetContent(*elements_arr); + if (maybe_result->IsFailure()) return maybe_result; // Set body.elements. Handle elements_sym = factory->LookupAsciiSymbol("elements"); @@ -1462,7 +1464,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, Handle summary_obj = factory->NewJSObject(isolate->array_function()); if (summary_obj->IsFailure()) return Object::cast(*summary_obj); - Handle::cast(summary_obj)->SetContent(*summary_arr); + + maybe_result = Handle::cast(summary_obj)->SetContent(*summary_arr); + if (maybe_result->IsFailure()) return maybe_result; // Create the body object. Handle body = factory->NewJSObject(isolate->object_function()); @@ -1589,7 +1593,9 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { // Return the result as a JS array. Handle lols = factory->NewJSObject(isolate->array_function()); - Handle::cast(lols)->SetContent(*list); + + maybe_result = Handle::cast(lols)->SetContent(*list); + if (maybe_result->IsFailure()) return maybe_result; Handle result = factory->NewJSObject(isolate->object_function()); if (result->IsFailure()) return Object::cast(*result); diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index b3ff82a..fd89d57 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -3163,7 +3163,8 @@ void MacroAssembler::CopyBytes(Register src, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue)); } diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc index 3dd7827..c17a8e8 100644 --- a/src/mips/stub-cache-mips.cc +++ b/src/mips/stub-cache-mips.cc @@ -3471,6 +3471,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: diff --git a/src/objects-debug.cc b/src/objects-debug.cc index bb384cb..f62c828 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -268,7 +268,7 @@ void JSObject::JSObjectVerify() { (map()->inobject_properties() + properties()->length() - map()->NextFreePropertyIndex())); } - ASSERT_EQ(map()->has_fast_elements(), + ASSERT_EQ((map()->has_fast_elements() || map()->has_fast_smi_only_elements()), (elements()->map() == GetHeap()->fixed_array_map() || elements()->map() == GetHeap()->fixed_cow_array_map())); ASSERT(map()->has_fast_elements() == HasFastElements()); @@ -330,7 +330,8 @@ void FixedDoubleArray::FixedDoubleArrayVerify() { double value = get_scalar(i); ASSERT(!isnan(value) || (BitCast(value) == - BitCast(canonical_not_the_hole_nan_as_double()))); + BitCast(canonical_not_the_hole_nan_as_double())) || + ((BitCast(value) & Double::kSignMask) != 0)); } } } diff --git a/src/objects-inl.h b/src/objects-inl.h index 5fadaa9..3b3f625 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1300,14 +1300,82 @@ FixedArrayBase* JSObject::elements() { return static_cast(array); } +void JSObject::ValidateSmiOnlyElements() { +#if DEBUG + if (FLAG_smi_only_arrays && + map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) { + Heap* heap = GetHeap(); + // Don't use elements, since integrity checks will fail if there + // are filler pointers in the array. + FixedArray* fixed_array = + reinterpret_cast(READ_FIELD(this, kElementsOffset)); + Map* map = fixed_array->map(); + // Arrays that have been shifted in place can't be verified. + if (map != heap->raw_unchecked_one_pointer_filler_map() && + map != heap->raw_unchecked_two_pointer_filler_map() && + map != heap->free_space_map()) { + for (int i = 0; i < fixed_array->length(); i++) { + Object* current = fixed_array->get(i); + ASSERT(current->IsSmi() || current == heap->the_hole_value()); + } + } + } +#endif +} + + +MaybeObject* JSObject::EnsureCanContainNonSmiElements() { +#if DEBUG + ValidateSmiOnlyElements(); +#endif + if (FLAG_smi_only_arrays && + (map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS)) { + Object* obj; + MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + set_map(Map::cast(obj)); + } + return this; +} + + +MaybeObject* JSObject::EnsureCanContainElements(Object** objects, + uint32_t count) { + if (FLAG_smi_only_arrays && + map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) { + for (uint32_t i = 0; i < count; ++i) { + Object* current = *objects++; + if (!current->IsSmi() && current != GetHeap()->the_hole_value()) { + return EnsureCanContainNonSmiElements(); + } + } + } + return this; +} + + +MaybeObject* JSObject::EnsureCanContainElements(FixedArray* elements) { + if (FLAG_smi_only_arrays) { + Object** objects = reinterpret_cast( + FIELD_ADDR(elements, elements->OffsetOfElementAt(0))); + return EnsureCanContainElements(objects, elements->length()); + } else { + return this; + } +} + void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) { - ASSERT(map()->has_fast_elements() == + ASSERT((map()->has_fast_elements() || + map()->has_fast_smi_only_elements()) == (value->map() == GetHeap()->fixed_array_map() || value->map() == GetHeap()->fixed_cow_array_map())); ASSERT(map()->has_fast_double_elements() == value->IsFixedDoubleArray()); ASSERT(value->HasValidElements()); +#ifdef DEBUG + ValidateSmiOnlyElements(); +#endif WRITE_FIELD(this, kElementsOffset, value); CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, value, mode); } @@ -1320,7 +1388,7 @@ void JSObject::initialize_properties() { void JSObject::initialize_elements() { - ASSERT(map()->has_fast_elements()); + ASSERT(map()->has_fast_elements() || map()->has_fast_smi_only_elements()); ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array())); WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array()); } @@ -1328,9 +1396,11 @@ void JSObject::initialize_elements() { MaybeObject* JSObject::ResetElements() { Object* obj; - { MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + ElementsKind elements_kind = FLAG_smi_only_arrays + ? FAST_SMI_ONLY_ELEMENTS + : FAST_ELEMENTS; + MaybeObject* maybe_obj = GetElementsTransitionMap(elements_kind); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; set_map(Map::cast(obj)); initialize_elements(); return this; @@ -1686,7 +1756,7 @@ void FixedDoubleArray::Initialize(FixedDoubleArray* from) { void FixedDoubleArray::Initialize(FixedArray* from) { int old_length = from->length(); - ASSERT(old_length < 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()) { @@ -3957,15 +4027,20 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) { ElementsKind JSObject::GetElementsKind() { ElementsKind kind = map()->elements_kind(); - ASSERT((kind == FAST_ELEMENTS && - (elements()->map() == GetHeap()->fixed_array_map() || - elements()->map() == GetHeap()->fixed_cow_array_map())) || +#if DEBUG + FixedArrayBase* fixed_array = + reinterpret_cast(READ_FIELD(this, kElementsOffset)); + Map* map = fixed_array->map(); + ASSERT(((kind == FAST_ELEMENTS || kind == FAST_SMI_ONLY_ELEMENTS) && + (map == GetHeap()->fixed_array_map() || + map == GetHeap()->fixed_cow_array_map())) || (kind == FAST_DOUBLE_ELEMENTS && - elements()->IsFixedDoubleArray()) || + fixed_array->IsFixedDoubleArray()) || (kind == DICTIONARY_ELEMENTS && - elements()->IsFixedArray() && - elements()->IsDictionary()) || + fixed_array->IsFixedArray() && + fixed_array->IsDictionary()) || (kind > DICTIONARY_ELEMENTS)); +#endif return kind; } @@ -3980,6 +4055,18 @@ bool JSObject::HasFastElements() { } +bool JSObject::HasFastSmiOnlyElements() { + return GetElementsKind() == FAST_SMI_ONLY_ELEMENTS; +} + + +bool JSObject::HasFastTypeElements() { + ElementsKind elements_kind = GetElementsKind(); + return elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_ELEMENTS; +} + + bool JSObject::HasFastDoubleElements() { return GetElementsKind() == FAST_DOUBLE_ELEMENTS; } @@ -3990,6 +4077,11 @@ bool JSObject::HasDictionaryElements() { } +bool JSObject::HasNonStrictArgumentsElements() { + return GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS; +} + + bool JSObject::HasExternalArrayElements() { HeapObject* array = elements(); ASSERT(array != NULL); @@ -4041,7 +4133,7 @@ bool JSObject::AllowsSetElementsLength() { MaybeObject* JSObject::EnsureWritableFastElements() { - ASSERT(HasFastElements()); + ASSERT(HasFastTypeElements()); FixedArray* elems = FixedArray::cast(elements()); Isolate* isolate = GetIsolate(); if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems; @@ -4409,7 +4501,7 @@ void Map::ClearCodeCache(Heap* heap) { void JSArray::EnsureSize(int required_size) { - ASSERT(HasFastElements()); + ASSERT(HasFastTypeElements()); FixedArray* elts = FixedArray::cast(elements()); const int kArraySizeThatFitsComfortablyInNewSpace = 128; if (elts->length() < required_size) { @@ -4432,9 +4524,12 @@ void JSArray::set_length(Smi* length) { } -void JSArray::SetContent(FixedArray* storage) { +MaybeObject* JSArray::SetContent(FixedArray* storage) { + MaybeObject* maybe_object = EnsureCanContainElements(storage); + if (maybe_object->IsFailure()) return maybe_object; set_length(Smi::FromInt(storage->length())); set_elements(storage); + return this; } diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 7d8f133..bb80392 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -82,6 +82,9 @@ void HeapObject::HeapObjectPrint(FILE* out) { case HEAP_NUMBER_TYPE: HeapNumber::cast(this)->HeapNumberPrint(out); break; + case FIXED_DOUBLE_ARRAY_TYPE: + FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out); + break; case FIXED_ARRAY_TYPE: FixedArray::cast(this)->FixedArrayPrint(out); break; @@ -242,6 +245,54 @@ void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) { } +static void PrintElementsKind(FILE* out, ElementsKind kind) { + switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: + PrintF(out, "FAST_SMI_ONLY_ELEMENTS"); + break; + case FAST_ELEMENTS: + PrintF(out, "FAST_ELEMENTS"); + break; + case FAST_DOUBLE_ELEMENTS: + PrintF(out, "FAST_DOUBLE_ELEMENTS"); + break; + case DICTIONARY_ELEMENTS: + PrintF(out, "DICTIONARY_ELEMENTS"); + break; + case NON_STRICT_ARGUMENTS_ELEMENTS: + PrintF(out, "NON_STRICT_ARGUMENTS_ELEMENTS"); + break; + case EXTERNAL_BYTE_ELEMENTS: + PrintF(out, "EXTERNAL_BYTE_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_BYTE_ELEMENTS"); + break; + case EXTERNAL_SHORT_ELEMENTS: + PrintF(out, "EXTERNAL_SHORT_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_SHORT_ELEMENTS"); + break; + case EXTERNAL_INT_ELEMENTS: + PrintF(out, "EXTERNAL_INT_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_INT_ELEMENTS"); + break; + case EXTERNAL_FLOAT_ELEMENTS: + PrintF(out, "EXTERNAL_FLOAT_ELEMENTS"); + break; + case EXTERNAL_DOUBLE_ELEMENTS: + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS"); + break; + case EXTERNAL_PIXEL_ELEMENTS: + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS"); + break; + } +} + + void JSObject::PrintProperties(FILE* out) { if (HasFastProperties()) { DescriptorArray* descs = map()->instance_descriptors(); @@ -265,16 +316,19 @@ void JSObject::PrintProperties(FILE* out) { PrintF(out, " (callback)\n"); break; case ELEMENTS_TRANSITION: - PrintF(out, " (elements transition)\n"); + PrintF(out, "(elements transition to "); + PrintElementsKind(out, + Map::cast(descs->GetValue(i))->elements_kind()); + PrintF(out, ")\n"); break; case MAP_TRANSITION: - PrintF(out, " (map transition)\n"); + PrintF(out, "(map transition)\n"); break; case CONSTANT_TRANSITION: - PrintF(out, " (constant transition)\n"); + PrintF(out, "(constant transition)\n"); break; case NULL_DESCRIPTOR: - PrintF(out, " (null descriptor)\n"); + PrintF(out, "(null descriptor)\n"); break; default: UNREACHABLE(); @@ -288,7 +342,10 @@ void JSObject::PrintProperties(FILE* out) { void JSObject::PrintElements(FILE* out) { - switch (GetElementsKind()) { + // Don't call GetElementsKind, its validation code can cause the printer to + // fail when debugging. + switch (map()->elements_kind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { // Print in array notation for non-sparse arrays. FixedArray* p = FixedArray::cast(elements()); @@ -396,8 +453,13 @@ void JSObject::PrintElements(FILE* out) { void JSObject::JSObjectPrint(FILE* out) { PrintF(out, "%p: [JSObject]\n", reinterpret_cast(this)); - PrintF(out, " - map = %p\n", reinterpret_cast(map())); - PrintF(out, " - prototype = %p\n", reinterpret_cast(GetPrototype())); + PrintF(out, " - map = %p [", reinterpret_cast(map())); + // Don't call GetElementsKind, its validation code can cause the printer to + // fail when debugging. + PrintElementsKind(out, this->map()->elements_kind()); + PrintF(out, + "]\n - prototype = %p\n", + reinterpret_cast(GetPrototype())); PrintF(out, " {\n"); PrintProperties(out); PrintElements(out); @@ -528,6 +590,16 @@ void FixedArray::FixedArrayPrint(FILE* out) { } +void FixedDoubleArray::FixedDoubleArrayPrint(FILE* out) { + HeapObject::PrintHeader(out, "FixedDoubleArray"); + PrintF(out, " - length: %d", length()); + for (int i = 0; i < length(); i++) { + PrintF(out, "\n [%d]: %g", i, get_scalar(i)); + } + PrintF(out, "\n"); +} + + void JSValue::JSValuePrint(FILE* out) { HeapObject::PrintHeader(out, "ValueObject"); value()->Print(out); diff --git a/src/objects.cc b/src/objects.cc index 94671d0..f894bc5 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -3063,6 +3063,7 @@ MaybeObject* JSObject::NormalizeElements() { if (array->IsDictionary()) return array; ASSERT(HasFastElements() || + HasFastSmiOnlyElements() || HasFastDoubleElements() || HasFastArgumentsElements()); // Compute the effective length and allocate a new backing store. @@ -3097,7 +3098,8 @@ MaybeObject* JSObject::NormalizeElements() { if (!maybe_value_object->ToObject(&value)) return maybe_value_object; } } else { - ASSERT(old_map->has_fast_elements()); + ASSERT(old_map->has_fast_elements() || + old_map->has_fast_smi_only_elements()); value = FixedArray::cast(array)->get(i); } PropertyDetails details = PropertyDetails(NONE, NORMAL); @@ -3412,7 +3414,8 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { bool JSObject::ReferencesObjectFromElements(FixedArray* elements, ElementsKind kind, Object* object) { - ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS); + ASSERT(kind == FAST_ELEMENTS || + kind == DICTIONARY_ELEMENTS); if (kind == FAST_ELEMENTS) { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() @@ -3467,6 +3470,8 @@ bool JSObject::ReferencesObject(Object* obj) { // Raw pixels and external arrays do not reference other // objects. break; + case FAST_SMI_ONLY_ELEMENTS: + break; case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: { FixedArray* elements = FixedArray::cast(this->elements()); @@ -3758,6 +3763,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, if (is_element) { switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: break; @@ -4007,6 +4013,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { // Accessors overwrite previous callbacks (cf. with getters/setters). switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: break; @@ -7416,8 +7423,10 @@ static void CopySlowElementsToFast(NumberDictionary* source, } -MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, - int length) { +MaybeObject* JSObject::SetFastElementsCapacityAndLength( + int capacity, + int length, + SetFastElementsCapacityMode set_capacity_mode) { Heap* heap = GetHeap(); // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); @@ -7434,15 +7443,25 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, Map* new_map = NULL; if (elements()->map() != heap->non_strict_arguments_elements_map()) { Object* object; - MaybeObject* maybe = GetElementsTransitionMap(FAST_ELEMENTS); + bool has_fast_smi_only_elements = + FLAG_smi_only_arrays && + (set_capacity_mode == kAllowSmiOnlyElements) && + (elements()->map()->has_fast_smi_only_elements() || + elements() == heap->empty_fixed_array()); + ElementsKind elements_kind = has_fast_smi_only_elements + ? FAST_SMI_ONLY_ELEMENTS + : FAST_ELEMENTS; + MaybeObject* maybe = GetElementsTransitionMap(elements_kind); if (!maybe->ToObject(&object)) return maybe; new_map = Map::cast(object); } - switch (GetElementsKind()) { + ElementsKind elements_kind = GetElementsKind(); + switch (elements_kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { AssertNoAllocation no_gc; - WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); + WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc)); CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode); set_map(new_map); set_elements(new_elements); @@ -7545,6 +7564,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( AssertNoAllocation no_gc; switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { elems->Initialize(FixedArray::cast(elements())); break; @@ -7582,8 +7602,9 @@ MaybeObject* JSObject::SetSlowElements(Object* len) { uint32_t new_length = static_cast(len->Number()); switch (GetElementsKind()) { - case FAST_ELEMENTS: { - case FAST_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: { // Make sure we never try to shrink dense arrays into sparse arrays. ASSERT(static_cast( FixedArrayBase::cast(elements())->length()) <= new_length); @@ -7649,7 +7670,7 @@ void JSArray::Expand(int required_size) { Handle new_backing = FACTORY->NewFixedArray(new_size); // Can't use this any more now because we may have had a GC! for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i)); - self->SetContent(*new_backing); + GetIsolate()->factory()->SetContent(self, new_backing); } @@ -7672,13 +7693,15 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { if (value < 0) return ArrayLengthRangeError(GetHeap()); ElementsKind elements_kind = GetElementsKind(); switch (elements_kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { int old_capacity = FixedArrayBase::cast(elements())->length(); if (value <= old_capacity) { if (IsJSArray()) { Object* obj; - if (elements_kind == FAST_ELEMENTS) { + if (elements_kind == FAST_ELEMENTS || + elements_kind == FAST_SMI_ONLY_ELEMENTS) { MaybeObject* maybe_obj = EnsureWritableFastElements(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } @@ -7689,7 +7712,8 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { } else { Address filler_start; int filler_size; - if (GetElementsKind() == FAST_ELEMENTS) { + if (elements_kind == FAST_ELEMENTS || + elements_kind == FAST_SMI_ONLY_ELEMENTS) { FixedArray* fast_elements = FixedArray::cast(elements()); fast_elements->set_length(value); filler_start = fast_elements->address() + @@ -7709,13 +7733,14 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { } else { // Otherwise, fill the unused tail with holes. int old_length = FastD2I(JSArray::cast(this)->length()->Number()); - if (GetElementsKind() == FAST_ELEMENTS) { + if (elements_kind == FAST_ELEMENTS || + elements_kind == FAST_SMI_ONLY_ELEMENTS) { FixedArray* fast_elements = FixedArray::cast(elements()); for (int i = value; i < old_length; i++) { fast_elements->set_the_hole(i); } } else { - ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); + ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS); FixedDoubleArray* fast_double_elements = FixedDoubleArray::cast(elements()); for (int i = value; i < old_length; i++) { @@ -7731,10 +7756,17 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { int new_capacity = value > min ? value : min; if (!ShouldConvertToSlowElements(new_capacity)) { MaybeObject* result; - if (GetElementsKind() == FAST_ELEMENTS) { - result = SetFastElementsCapacityAndLength(new_capacity, value); + if (elements_kind == FAST_ELEMENTS || + elements_kind == FAST_SMI_ONLY_ELEMENTS) { + SetFastElementsCapacityMode set_capacity_mode = + elements_kind == FAST_SMI_ONLY_ELEMENTS + ? kAllowSmiOnlyElements + : kDontAllowSmiOnlyElements; + result = SetFastElementsCapacityAndLength(new_capacity, + value, + set_capacity_mode); } else { - ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); + ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS); result = SetFastDoubleElementsCapacityAndLength(new_capacity, value); } @@ -7791,10 +7823,13 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { // len is not a number so make the array size one and // set only element to len. Object* obj; - { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; FixedArray::cast(obj)->set(0, len); + + maybe_obj = EnsureCanContainElements(&len, 1); + if (maybe_obj->IsFailure()) return maybe_obj; + if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1)); set_elements(FixedArray::cast(obj)); return this; @@ -7942,8 +7977,16 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, } +MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, + uint32_t first_arg, + uint32_t arg_count) { + return EnsureCanContainElements(args->arguments() - first_arg, arg_count); +} + + bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast @@ -8085,6 +8128,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast @@ -8199,6 +8243,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { ElementsKind kind = GetElementsKind(); switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast @@ -8453,7 +8498,8 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value, StrictModeFlag strict_mode, bool check_prototype) { - ASSERT(HasFastElements() || HasFastArgumentsElements()); + ASSERT(HasFastTypeElements() || + HasFastArgumentsElements()); FixedArray* backing_store = FixedArray::cast(elements()); if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) { @@ -8478,6 +8524,24 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, // Check whether there is extra space in fixed array. if (index < length) { + if (HasFastSmiOnlyElements()) { + if (!value->IsSmi()) { + // If the value is a number, transition from smi-only to + // FastDoubleElements. + if (value->IsNumber()) { + MaybeObject* maybe = + SetFastDoubleElementsCapacityAndLength(length, length); + if (maybe->IsFailure()) return maybe; + FixedDoubleArray::cast(elements())->set(index, value->Number()); + return value; + } + // Value is not a number, transition to generic fast elements. + MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS); + Map* new_map; + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + set_map(new_map); + } + } backing_store->set(index, value); if (IsJSArray()) { // Update the length of the array if needed. @@ -8497,8 +8561,14 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, if (!ShouldConvertToSlowElements(new_capacity)) { ASSERT(static_cast(new_capacity) > index); Object* new_elements; + SetFastElementsCapacityMode set_capacity_mode = + value->IsSmi() && HasFastSmiOnlyElements() + ? kAllowSmiOnlyElements + : kDontAllowSmiOnlyElements; MaybeObject* maybe = - SetFastElementsCapacityAndLength(new_capacity, index + 1); + SetFastElementsCapacityAndLength(new_capacity, + index + 1, + set_capacity_mode); if (!maybe->ToObject(&new_elements)) return maybe; FixedArray::cast(new_elements)->set(index, value); return value; @@ -8604,7 +8674,9 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } MaybeObject* result = CanConvertToFastDoubleElements() ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) - : SetFastElementsCapacityAndLength(new_length, new_length); + : SetFastElementsCapacityAndLength(new_length, + new_length, + kDontAllowSmiOnlyElements); if (result->IsFailure()) return result; #ifdef DEBUG if (FLAG_trace_normalization) { @@ -8648,10 +8720,15 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( if (IsJSArray()) { CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); } - MaybeObject* maybe_obj = - SetFastElementsCapacityAndLength(elms_length, length); + MaybeObject* maybe_obj = SetFastElementsCapacityAndLength( + elms_length, + length, + kDontAllowSmiOnlyElements); if (!maybe_obj->ToObject(&obj)) return maybe_obj; - return SetFastElement(index, value, strict_mode, check_prototype); + return SetFastElement(index, + value, + strict_mode, + check_prototype); } double double_value = value_is_smi @@ -8759,6 +8836,7 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, bool check_prototype) { Isolate* isolate = GetIsolate(); switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: return SetFastElement(index, value, strict_mode, check_prototype); case FAST_DOUBLE_ELEMENTS: @@ -8921,6 +8999,7 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { break; } // Fall through. + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: backing_store = FixedArray::cast(backing_store_base); *capacity = backing_store->length(); @@ -9196,6 +9275,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) { if (this->IsStringObjectWithCharacterAt(index)) return true; switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast( @@ -9435,6 +9515,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, PropertyAttributes filter) { int counter = 0; switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : @@ -10337,7 +10418,8 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } } - ASSERT(HasFastElements() || HasFastDoubleElements()); + ASSERT(HasFastTypeElements() || + HasFastDoubleElements()); // Collect holes at the end, undefined before that and the rest at the // start, and return the number of non-hole, non-undefined values. diff --git a/src/objects.h b/src/objects.h index 38e9add..9711245 100644 --- a/src/objects.h +++ b/src/objects.h @@ -137,8 +137,13 @@ namespace v8 { namespace internal { enum ElementsKind { - // The "fast" kind for tagged values. Must be first to make it possible - // to efficiently check maps if they have fast elements. + // The "fast" kind for elements that only contain SMI values. Must be first + // to make it possible to efficiently check maps for this kind. + FAST_SMI_ONLY_ELEMENTS, + + // The "fast" kind for tagged values. Must be second to make it possible to + // efficiently check maps for this and the FAST_SMI_ONLY_ELEMENTS kind + // together at once. FAST_ELEMENTS, // The "fast" kind for unwrapped, non-tagged double values. @@ -161,7 +166,7 @@ enum ElementsKind { // Derived constants from ElementsKind FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, - FIRST_ELEMENTS_KIND = FAST_ELEMENTS, + FIRST_ELEMENTS_KIND = FAST_SMI_ONLY_ELEMENTS, LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS }; @@ -1432,8 +1437,14 @@ class JSObject: public JSReceiver { MUST_USE_RESULT inline MaybeObject* ResetElements(); inline ElementsKind GetElementsKind(); inline ElementsAccessor* GetElementsAccessor(); + inline bool HasFastSmiOnlyElements(); inline bool HasFastElements(); + // Returns if an object has either FAST_ELEMENT or FAST_SMI_ONLY_ELEMENT + // elements. TODO(danno): Rename HasFastTypeElements to HasFastElements() and + // HasFastElements to HasFastObjectElements. + inline bool HasFastTypeElements(); inline bool HasFastDoubleElements(); + inline bool HasNonStrictArgumentsElements(); inline bool HasDictionaryElements(); inline bool HasExternalPixelElements(); inline bool HasExternalArrayElements(); @@ -1608,6 +1619,19 @@ class JSObject: public JSReceiver { // Tests for the fast common case for property enumeration. bool IsSimpleEnum(); + inline void ValidateSmiOnlyElements(); + + // Makes sure that this object can contain non-smi Object as elements. + inline MaybeObject* EnsureCanContainNonSmiElements(); + + // Makes sure that this object can contain the specified elements. + inline MaybeObject* EnsureCanContainElements(Object** elements, + uint32_t count); + inline MaybeObject* EnsureCanContainElements(FixedArray* elements); + MaybeObject* EnsureCanContainElements(Arguments* arguments, + uint32_t first_arg, + uint32_t arg_count); + // Do we want to keep the elements in fast case when increasing the // capacity? bool ShouldConvertToSlowElements(int new_capacity); @@ -1656,6 +1680,7 @@ class JSObject: public JSReceiver { Object* value, StrictModeFlag strict_mode, bool check_prototype); + MUST_USE_RESULT MaybeObject* SetDictionaryElement(uint32_t index, Object* value, StrictModeFlag strict_mode, @@ -1678,11 +1703,18 @@ class JSObject: public JSReceiver { // The undefined object if index is out of bounds. MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index); + enum SetFastElementsCapacityMode { + kAllowSmiOnlyElements, + kDontAllowSmiOnlyElements + }; + // Replace the elements' backing store with fast elements of the given // capacity. Update the length for JSArrays. Returns the new backing // store. - MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity, - int length); + MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength( + int capacity, + int length, + SetFastElementsCapacityMode set_capacity_mode); MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength( int capacity, int length); @@ -3980,8 +4012,12 @@ class Map: public HeapObject { (bit_field2() & kElementsKindMask) >> kElementsKindShift); } + // Tells whether the instance has fast elements that are only Smis. + inline bool has_fast_smi_only_elements() { + return elements_kind() == FAST_SMI_ONLY_ELEMENTS; + } + // Tells whether the instance has fast elements. - // Equivalent to instance->GetElementsKind() == FAST_ELEMENTS. inline bool has_fast_elements() { return elements_kind() == FAST_ELEMENTS; } @@ -3990,6 +4026,10 @@ class Map: public HeapObject { return elements_kind() == FAST_DOUBLE_ELEMENTS; } + inline bool has_non_strict_arguments_elements() { + return elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS; + } + inline bool has_external_array_elements() { ElementsKind kind(elements_kind()); return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && @@ -4237,7 +4277,7 @@ class Map: public HeapObject { static const int kStringWrapperSafeForDefaultValueOf = 2; static const int kAttachedToSharedFunctionInfo = 3; // No bits can be used after kElementsKindFirstBit, they are all reserved for - // storing ElementKind. for anything other than storing the ElementKind. + // storing ElementKind. static const int kElementsKindShift = 4; static const int kElementsKindBitCount = 4; @@ -4246,6 +4286,9 @@ class Map: public HeapObject { ((1 << (kElementsKindShift + kElementsKindBitCount)) - 1); static const int8_t kMaximumBitField2FastElementValue = static_cast( (FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1; + static const int8_t kMaximumBitField2FastSmiOnlyElementValue = + static_cast((FAST_SMI_ONLY_ELEMENTS + 1) << + Map::kElementsKindShift) - 1; // Bit positions for bit field 3 static const int kIsShared = 0; @@ -6861,7 +6904,7 @@ class JSArray: public JSObject { MUST_USE_RESULT MaybeObject* Initialize(int capacity); // Set the content of the array to the content of storage. - inline void SetContent(FixedArray* storage); + inline MaybeObject* SetContent(FixedArray* storage); // Casting. static inline JSArray* cast(Object* obj); diff --git a/src/runtime.cc b/src/runtime.cc index a51fcd3..cb63072 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -177,6 +177,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, // Pixel elements cannot be created using an object literal. ASSERT(!copy->HasExternalArrayElements()); switch (copy->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); if (elements->map() == heap->fixed_cow_array_map()) { @@ -189,6 +190,9 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } else { for (int i = 0; i < elements->length(); i++) { Object* value = elements->get(i); + ASSERT(value->IsSmi() || + value->IsTheHole() || + (copy->GetElementsKind() == FAST_ELEMENTS)); if (value->IsJSObject()) { JSObject* js_object = JSObject::cast(value); { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, @@ -432,16 +436,28 @@ static Handle CreateArrayLiteralBoilerplate( is_cow ? elements : isolate->factory()->CopyFixedArray(elements); Handle content = Handle::cast(copied_elements); + bool has_non_smi = false; if (is_cow) { -#ifdef DEBUG // Copy-on-write arrays must be shallow (and simple). - for (int i = 0; i < content->length(); i++) { - ASSERT(!content->get(i)->IsFixedArray()); - } + if (FLAG_smi_only_arrays) { + for (int i = 0; i < content->length(); i++) { + Object* current = content->get(i); + ASSERT(!current->IsFixedArray()); + if (!current->IsSmi()) { + has_non_smi = true; + } + } + } else { +#if DEBUG + for (int i = 0; i < content->length(); i++) { + ASSERT(!content->get(i)->IsFixedArray()); + } #endif + } } else { for (int i = 0; i < content->length(); i++) { - if (content->get(i)->IsFixedArray()) { + Object* current = content->get(i); + if (current->IsFixedArray()) { // The value contains the constant_properties of a // simple object or array literal. Handle fa(FixedArray::cast(content->get(i))); @@ -449,12 +465,25 @@ static Handle CreateArrayLiteralBoilerplate( CreateLiteralBoilerplate(isolate, literals, fa); if (result.is_null()) return result; content->set(i, *result); + has_non_smi = true; + } else { + if (!current->IsSmi()) { + has_non_smi = true; + } } } } // Set the elements. - Handle::cast(object)->SetContent(*content); + Handle js_object(Handle::cast(object)); + isolate->factory()->SetContent(js_object, content); + + if (FLAG_smi_only_arrays) { + if (has_non_smi && js_object->HasFastSmiOnlyElements()) { + isolate->factory()->EnsureCanContainNonSmiElements(js_object); + } + } + return object; } @@ -1631,6 +1660,19 @@ RUNTIME_FUNCTION(MaybeObject*, } +RUNTIME_FUNCTION(MaybeObject*, Runtime_NonSmiElementStored) { + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSObject, object, 0); + if (FLAG_smi_only_arrays && object->HasFastSmiOnlyElements()) { + MaybeObject* maybe_map = object->GetElementsTransitionMap(FAST_ELEMENTS); + Map* map; + if (!maybe_map->To(&map)) return maybe_map; + object->set_map(Map::cast(map)); + } + return *object; +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) { HandleScope scope(isolate); ASSERT(args.length() == 4); @@ -2214,7 +2256,8 @@ class FixedArrayBuilder { public: explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity) : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)), - length_(0) { + length_(0), + has_non_smi_elements_(false) { // Require a non-zero initial size. Ensures that doubling the size to // extend the array will work. ASSERT(initial_capacity > 0); @@ -2222,7 +2265,8 @@ class FixedArrayBuilder { explicit FixedArrayBuilder(Handle backing_store) : array_(backing_store), - length_(0) { + length_(0), + has_non_smi_elements_(false) { // Require a non-zero initial size. Ensures that doubling the size to // extend the array will work. ASSERT(backing_store->length() > 0); @@ -2250,12 +2294,15 @@ class FixedArrayBuilder { } void Add(Object* value) { + ASSERT(!value->IsSmi()); ASSERT(length_ < capacity()); array_->set(length_, value); length_++; + has_non_smi_elements_ = true; } void Add(Smi* value) { + ASSERT(value->IsSmi()); ASSERT(length_ < capacity()); array_->set(length_, value); length_++; @@ -2280,7 +2327,7 @@ class FixedArrayBuilder { } Handle ToJSArray(Handle target_array) { - target_array->set_elements(*array_); + FACTORY->SetContent(target_array, array_); target_array->set_length(Smi::FromInt(length_)); return target_array; } @@ -2288,6 +2335,7 @@ class FixedArrayBuilder { private: Handle array_; int length_; + bool has_non_smi_elements_; }; @@ -6121,6 +6169,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { int part_count = indices.length(); Handle result = isolate->factory()->NewJSArray(part_count); + MaybeObject* maybe_result = result->EnsureCanContainNonSmiElements(); + if (maybe_result->IsFailure()) return maybe_result; result->set_length(Smi::FromInt(part_count)); ASSERT(result->HasFastElements()); @@ -6495,6 +6545,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { // This assumption is used by the slice encoding in one or two smis. ASSERT(Smi::kMaxValue >= String::kMaxLength); + MaybeObject* maybe_result = array->EnsureCanContainNonSmiElements(); + if (maybe_result->IsFailure()) return maybe_result; + int special_length = special->length(); if (!array->HasFastElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); @@ -6722,7 +6775,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { NoHandleAllocation ha; ASSERT(args.length() == 3); CONVERT_CHECKED(JSArray, elements_array, args[0]); - RUNTIME_ASSERT(elements_array->HasFastElements()); + RUNTIME_ASSERT(elements_array->HasFastElements() || + elements_array->HasFastSmiOnlyElements()); CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); CONVERT_CHECKED(String, separator, args[2]); // elements_array is fast-mode JSarray of alternating positions @@ -7889,7 +7943,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) { int bound_argc = 0; if (!args[1]->IsNull()) { CONVERT_ARG_CHECKED(JSArray, params, 1); - RUNTIME_ASSERT(params->HasFastElements()); + RUNTIME_ASSERT(params->HasFastTypeElements()); bound_args = Handle(FixedArray::cast(params->elements())); bound_argc = Smi::cast(params->length())->value(); } @@ -8984,6 +9038,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) { FlattenString(str); CONVERT_ARG_CHECKED(JSArray, output, 1); + + MaybeObject* maybe_result_array = + output->EnsureCanContainNonSmiElements(); + if (maybe_result_array->IsFailure()) return maybe_result_array; RUNTIME_ASSERT(output->HasFastElements()); AssertNoAllocation no_allocation; @@ -9287,7 +9345,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) { ASSERT(args.length() == 2); CONVERT_CHECKED(JSArray, array, args[0]); CONVERT_CHECKED(JSObject, element, args[1]); - RUNTIME_ASSERT(array->HasFastElements()); + RUNTIME_ASSERT(array->HasFastElements() || array->HasFastSmiOnlyElements()); int length = Smi::cast(array->length())->value(); FixedArray* elements = FixedArray::cast(array->elements()); for (int i = 0; i < length; i++) { @@ -9518,6 +9576,7 @@ static void CollectElementIndices(Handle object, List* indices) { ElementsKind kind = object->GetElementsKind(); switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { Handle elements(FixedArray::cast(object->elements())); uint32_t length = static_cast(elements->length()); @@ -9637,6 +9696,7 @@ static bool IterateElements(Isolate* isolate, ArrayConcatVisitor* visitor) { uint32_t length = static_cast(receiver->length()->Number()); switch (receiver->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. @@ -9960,7 +10020,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) { } return *isolate->factory()->NewJSArrayWithElements(keys); } else { - ASSERT(array->HasFastElements() || array->HasFastDoubleElements()); + ASSERT(array->HasFastElements() || + array->HasFastSmiOnlyElements() || + array->HasFastDoubleElements()); Handle single_interval = isolate->factory()->NewFixedArray(2); // -1 means start of array. single_interval->set(0, Smi::FromInt(-1)); @@ -11870,7 +11932,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) { // Return result as a JS array. Handle result = isolate->factory()->NewJSObject(isolate->array_function()); - Handle::cast(result)->SetContent(*instances); + isolate->factory()->SetContent(Handle::cast(result), instances); return *result; } @@ -11998,12 +12060,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) { // Return result as JS array. Object* result; - { MaybeObject* maybe_result = isolate->heap()->AllocateJSObject( + MaybeObject* maybe_result = isolate->heap()->AllocateJSObject( isolate->context()->global_context()->array_function()); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - JSArray::cast(result)->SetContent(instances); - return result; + if (!maybe_result->ToObject(&result)) return maybe_result; + return JSArray::cast(result)->SetContent(instances); } @@ -12084,8 +12144,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) { isolate->context()->global_context()->array_function()); if (!maybe_result->ToObject(&result)) return maybe_result; } - JSArray::cast(result)->SetContent(instances); - return result; + return JSArray::cast(result)->SetContent(instances); } @@ -13057,6 +13116,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IS_VAR) { return isolate->heap()->ToBoolean(obj->Has##Name()); \ } +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOnlyElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements) diff --git a/src/runtime.h b/src/runtime.h index 1538b7d..c6f7773 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -330,6 +330,8 @@ namespace internal { F(InitializeConstContextSlot, 3, 1) \ F(OptimizeObjectForAddingMultipleProperties, 2, 1) \ \ + /* Arrays */ \ + F(NonSmiElementStored, 1, 1) \ /* Debugging */ \ F(DebugPrint, 1, 1) \ F(DebugTrace, 0, 1) \ @@ -354,6 +356,7 @@ namespace internal { F(IS_VAR, 1, 1) \ \ /* expose boolean functions from objects-inl.h */ \ + F(HasFastSmiOnlyElements, 1, 1) \ F(HasFastElements, 1, 1) \ F(HasFastDoubleElements, 1, 1) \ F(HasDictionaryElements, 1, 1) \ diff --git a/src/stub-cache.h b/src/stub-cache.h index 18c157b..fcb58e1 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -704,7 +704,8 @@ class KeyedStoreStubCompiler: public StubCompiler { CodeList* handler_ics); static void GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array); + bool is_js_array, + ElementsKind element_kind); static void GenerateStoreFastDoubleElement(MacroAssembler* masm, bool is_js_array); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 350216a..e000fef 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2396,6 +2396,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3121,6 +3122,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index e27f6c9..b73e854 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2648,7 +2648,8 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); cmpb(FieldOperand(map, Map::kBitField2Offset), Immediate(Map::kMaximumBitField2FastElementValue)); j(above, fail, distance); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 2ef6178..584c549 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -3464,6 +3464,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3531,6 +3532,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3662,8 +3664,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -3700,6 +3704,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ movq(Operand(rcx, 0), rax); // Make sure to preserve the value in register rax. __ movq(rdx, rax); + ASSERT(elements_kind == FAST_ELEMENTS); __ RecordWrite(rdi, rcx, rdx, kDontSaveFPRegs); // Done. diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index aeab401..9a2e017 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -672,7 +672,8 @@ TEST(JSArray) { // Set array length to 0. ok = array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); CHECK_EQ(Smi::FromInt(0), array->length()); - CHECK(array->HasFastElements()); // Must be in fast mode. + // Must be in fast mode. + CHECK(array->HasFastTypeElements()); // array[length] = name. ok = array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked(); diff --git a/test/mjsunit/element-kind.js b/test/mjsunit/element-kind.js index 48a029f..1949857 100644 --- a/test/mjsunit/element-kind.js +++ b/test/mjsunit/element-kind.js @@ -28,7 +28,10 @@ // Flags: --allow-natives-syntax // Test element kind of objects +support_smi_only_arrays = %HasFastSmiOnlyElements([]); + var element_kind = { + fast_smi_only_elements : 0, fast_elements : 1, fast_double_elements : 2, dictionary_elements : 3, @@ -45,8 +48,16 @@ var element_kind = { // We expect an object to only be of one element kind. function assertKind(expected, obj){ - assertEquals(expected == element_kind.fast_elements, - %HasFastElements(obj)); + if (support_smi_only_arrays) { + assertEquals(expected == element_kind.fast_smi_only_elements, + %HasFastSmiOnlyElements(obj)); + assertEquals(expected == element_kind.fast_elements, + %HasFastElements(obj)); + } else { + assertEquals(expected == element_kind.fast_elements || + expected == element_kind.fast_smi_only_elements, + %HasFastElements(obj)); + } assertEquals(expected == element_kind.fast_double_elements, %HasFastDoubleElements(obj)); assertEquals(expected == element_kind.dictionary_elements, @@ -80,9 +91,22 @@ me.dance = 0xD15C0; me.drink = 0xC0C0A; assertKind(element_kind.fast_elements, me); +var too = [1,2,3]; +assertKind(element_kind.fast_smi_only_elements, too); +too.dance = 0xD15C0; +too.drink = 0xC0C0A; +assertKind(element_kind.fast_smi_only_elements, too); + +// Make sure the element kind transitions from smionly when a non-smi is stored. var you = new Array(); +assertKind(element_kind.fast_smi_only_elements, you); for(i = 0; i < 1337; i++) { - you[i] = i; + var val = i; + if (i == 1336) { + assertKind(element_kind.fast_smi_only_elements, you); + val = new Object(); + } + you[i] = val; } assertKind(element_kind.fast_elements, you);