From 9910edbb9a0ade8ea6f7ff6da0957a474c43f0d7 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Wed, 23 May 2012 14:24:29 +0000 Subject: [PATCH] Implement tracking and optimizations of packed arrays R=jkummerow@chromium.org TEST=jkummerow@chromium.org Review URL: https://chromiumcodereview.appspot.com/10170030 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11636 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/SConscript | 1 + src/api.cc | 2 +- src/api.h | 4 +- src/arm/builtins-arm.cc | 9 +- src/arm/code-stubs-arm.cc | 14 +- src/arm/codegen-arm.cc | 4 +- src/arm/full-codegen-arm.cc | 7 +- src/arm/ic-arm.cc | 14 +- src/arm/lithium-arm.cc | 5 +- src/arm/lithium-codegen-arm.cc | 48 ++- src/arm/macro-assembler-arm.cc | 86 +++-- src/arm/macro-assembler-arm.h | 9 +- src/arm/stub-cache-arm.cc | 50 ++- src/bootstrapper.cc | 6 +- src/builtins.cc | 87 +++-- src/code-stubs.cc | 32 +- src/codegen.h | 6 +- src/contexts.h | 20 +- src/elements-kind.cc | 134 +++++++ src/elements-kind.h | 209 +++++++++++ src/elements.cc | 370 +++++++++++++------ src/elements.h | 5 + src/factory.cc | 5 +- src/factory.h | 17 +- src/flag-definitions.h | 1 + src/heap.cc | 35 +- src/heap.h | 2 +- src/hydrogen-instructions.cc | 23 +- src/hydrogen-instructions.h | 87 +++-- src/hydrogen.cc | 114 +++--- src/hydrogen.h | 1 + src/ia32/builtins-ia32.cc | 9 +- src/ia32/code-stubs-ia32.cc | 16 +- src/ia32/codegen-ia32.cc | 4 +- src/ia32/full-codegen-ia32.cc | 16 +- src/ia32/ic-ia32.cc | 16 +- src/ia32/lithium-codegen-ia32.cc | 55 +-- src/ia32/lithium-ia32.cc | 5 +- src/ia32/macro-assembler-ia32.cc | 88 ++--- src/ia32/macro-assembler-ia32.h | 9 +- src/ia32/stub-cache-ia32.cc | 27 +- src/ic.cc | 65 +++- src/ic.h | 8 +- src/jsregexp.cc | 2 +- src/lithium.cc | 7 +- src/mips/builtins-mips.cc | 9 +- src/mips/code-stubs-mips.cc | 16 +- src/mips/codegen-mips.cc | 4 +- src/mips/full-codegen-mips.cc | 8 +- src/mips/ic-mips.cc | 23 +- src/mips/lithium-codegen-mips.cc | 45 ++- src/mips/lithium-mips.cc | 5 +- src/mips/macro-assembler-mips.cc | 87 +++-- src/mips/macro-assembler-mips.h | 9 +- src/mips/stub-cache-mips.cc | 51 ++- src/objects-debug.cc | 23 +- src/objects-inl.h | 234 +++++++------ src/objects-printer.cc | 5 +- src/objects.cc | 448 ++++++++++++++---------- src/objects.h | 107 +++--- src/parser.cc | 20 +- src/profile-generator.cc | 2 +- src/runtime.cc | 214 ++++++----- src/runtime.h | 6 +- src/string-stream.cc | 4 +- src/x64/builtins-x64.cc | 9 +- src/x64/code-stubs-x64.cc | 16 +- src/x64/codegen-x64.cc | 4 +- src/x64/full-codegen-x64.cc | 16 +- src/x64/ic-x64.cc | 14 +- src/x64/lithium-codegen-x64.cc | 61 ++-- src/x64/lithium-x64.cc | 5 +- src/x64/macro-assembler-x64.cc | 88 ++--- src/x64/macro-assembler-x64.h | 9 +- src/x64/stub-cache-x64.cc | 41 ++- test/cctest/test-heap.cc | 8 +- test/mjsunit/array-construct-transition.js | 6 +- test/mjsunit/array-literal-transitions.js | 20 +- test/mjsunit/elements-kind.js | 8 +- test/mjsunit/elements-transition-hoisting.js | 4 +- test/mjsunit/elements-transition.js | 10 +- test/mjsunit/packed-elements.js | 112 ++++++ test/mjsunit/regress/regress-117409.js | 2 +- test/mjsunit/regress/regress-1849.js | 6 +- test/mjsunit/regress/regress-1878.js | 4 +- test/mjsunit/regress/regress-crbug-122271.js | 8 +- test/mjsunit/regress/regress-smi-only-concat.js | 4 +- test/mjsunit/unbox-double-arrays.js | 4 +- tools/gyp/v8.gyp | 2 + 89 files changed, 2310 insertions(+), 1205 deletions(-) create mode 100644 src/elements-kind.cc create mode 100644 src/elements-kind.h create mode 100644 test/mjsunit/packed-elements.js diff --git a/src/SConscript b/src/SConscript index 0d0b535..2482b37 100755 --- a/src/SConscript +++ b/src/SConscript @@ -68,6 +68,7 @@ SOURCES = { diy-fp.cc dtoa.cc elements.cc + elements-kind.cc execution.cc factory.cc flags.cc diff --git a/src/api.cc b/src/api.cc index b43aaf3..74886f0 100644 --- a/src/api.cc +++ b/src/api.cc @@ -5040,7 +5040,7 @@ Local Array::CloneElementAt(uint32_t index) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ON_BAILOUT(isolate, "v8::Array::CloneElementAt()", return Local()); i::Handle self = Utils::OpenHandle(this); - if (!self->HasFastElements()) { + if (!self->HasFastObjectElements()) { return Local(); } i::FixedArray* elms = i::FixedArray::cast(self->elements()); diff --git a/src/api.h b/src/api.h index 3ad57f4..05e5e72 100644 --- a/src/api.h +++ b/src/api.h @@ -105,13 +105,13 @@ NeanderArray::NeanderArray(v8::internal::Handle obj) v8::internal::Object* NeanderObject::get(int offset) { - ASSERT(value()->HasFastElements()); + ASSERT(value()->HasFastObjectElements()); return v8::internal::FixedArray::cast(value()->elements())->get(offset); } void NeanderObject::set(int offset, v8::internal::Object* value) { - ASSERT(value_->HasFastElements()); + ASSERT(value_->HasFastObjectElements()); v8::internal::FixedArray::cast(value_->elements())->set(offset, value); } diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index c99e778..578bd81 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -114,7 +114,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Label* gc_required) { const int initial_capacity = JSArray::kPreallocatedArrayElements; STATIC_ASSERT(initial_capacity >= 0); - __ LoadInitialArrayMap(array_function, scratch2, scratch1); + __ LoadInitialArrayMap(array_function, scratch2, scratch1, false); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -208,7 +208,8 @@ static void AllocateJSArray(MacroAssembler* masm, bool fill_with_hole, Label* gc_required) { // Load the initial map from the array function. - __ LoadInitialArrayMap(array_function, scratch2, elements_array_storage); + __ LoadInitialArrayMap(array_function, scratch2, + elements_array_storage, fill_with_hole); if (FLAG_debug_code) { // Assert that array size is not zero. __ tst(array_size, array_size); @@ -440,10 +441,10 @@ static void ArrayNativeCode(MacroAssembler* masm, __ b(call_generic_code); __ bind(¬_double); - // Transition FAST_SMI_ONLY_ELEMENTS to FAST_ELEMENTS. + // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS. // r3: JSArray __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, r2, r9, diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 218c355..2296490 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -7102,8 +7102,8 @@ static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { // KeyedStoreStubCompiler::GenerateStoreFastElement. { REG(r3), REG(r2), REG(r4), EMIT_REMEMBERED_SET }, { REG(r2), REG(r3), REG(r4), EMIT_REMEMBERED_SET }, - // ElementsTransitionGenerator::GenerateSmiOnlyToObject - // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // ElementsTransitionGenerator::GenerateMapChangeElementTransition + // and ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(r2), REG(r3), REG(r9), EMIT_REMEMBERED_SET }, { REG(r2), REG(r3), REG(r9), OMIT_REMEMBERED_SET }, @@ -7366,9 +7366,9 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { Label fast_elements; __ CheckFastElements(r2, r5, &double_elements); - // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS __ JumpIfSmi(r0, &smi_element); - __ CheckFastSmiOnlyElements(r2, r5, &fast_elements); + __ CheckFastSmiElements(r2, r5, &fast_elements); // Store into the array literal requires a elements transition. Call into // the runtime. @@ -7380,7 +7380,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ Push(r5, r4); __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); - // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. __ bind(&fast_elements); __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset)); __ add(r6, r5, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); @@ -7391,8 +7391,8 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); __ Ret(); - // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or - // FAST_ELEMENTS, and value is Smi. + // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS, + // and value is Smi. __ bind(&smi_element); __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset)); __ add(r6, r5, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index befd8f2..e00afb9 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -73,7 +73,7 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { // ------------------------------------------------------------------------- // Code generators -void ElementsTransitionGenerator::GenerateSmiOnlyToObject( +void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : value @@ -96,7 +96,7 @@ void ElementsTransitionGenerator::GenerateSmiOnlyToObject( } -void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( +void ElementsTransitionGenerator::GenerateSmiToDouble( MacroAssembler* masm, Label* fail) { // ----------- S t a t e ------------- // -- r0 : value diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 3c8df29..2a5887a 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1701,7 +1701,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ASSERT_EQ(2, constant_elements->length()); ElementsKind constant_elements_kind = static_cast(Smi::cast(constant_elements->get(0))->value()); - bool has_fast_elements = constant_elements_kind == FAST_ELEMENTS; + bool has_fast_elements = IsFastObjectElementsKind(constant_elements_kind); Handle constant_elements_values( FixedArrayBase::cast(constant_elements->get(1))); @@ -1722,8 +1722,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - ASSERT(constant_elements_kind == FAST_ELEMENTS || - constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || FLAG_smi_only_arrays); FastCloneShallowArrayStub::Mode mode = has_fast_elements ? FastCloneShallowArrayStub::CLONE_ELEMENTS @@ -1751,7 +1750,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - if (constant_elements_kind == FAST_ELEMENTS) { + if (IsFastObjectElementsKind(constant_elements_kind)) { int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ ldr(r6, MemOperand(sp)); // Copy of array literal. __ ldr(r1, FieldMemOperand(r6, JSObject::kElementsOffset)); diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index c12c167..fd93480 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -1249,7 +1249,7 @@ void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { // Must return the modified receiver in r0. if (!FLAG_trace_elements_transitions) { Label fail; - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); __ mov(r0, r2); __ Ret(); __ bind(&fail); @@ -1462,27 +1462,27 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ CompareRoot(r4, Heap::kHeapNumberMapRootIndex); __ b(ne, &non_double_value); - // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // Value is a double. Transition FAST_SMI_ELEMENTS -> // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, r4, &slow); ASSERT(receiver_map.is(r3)); // Transition code expects map in r3 - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &slow); __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); __ jmp(&fast_double_without_map_check); __ bind(&non_double_value); - // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, receiver_map, r4, &slow); ASSERT(receiver_map.is(r3)); // Transition code expects map in r3 - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm); __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); __ jmp(&finish_object_store); diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 5c60f53..c97831a 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -2082,8 +2082,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoTransitionElementsKind( HTransitionElementsKind* instr) { - if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + ElementsKind from_kind = instr->original_map()->elements_kind(); + ElementsKind to_kind = instr->transitioned_map()->elements_kind(); + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { LOperand* object = UseRegister(instr->object()); LOperand* new_map_reg = TempRegister(); LTransitionElementsKind* result = diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index d224d24..fd4b3e8 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2696,8 +2696,10 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ ldr(scratch, FieldMemOperand(scratch, Map::kBitField2Offset)); __ ubfx(scratch, scratch, Map::kElementsKindShift, Map::kElementsKindBitCount); - __ cmp(scratch, Operand(FAST_ELEMENTS)); - __ b(eq, &done); + __ cmp(scratch, Operand(GetInitialFastElementsKind())); + __ b(lt, &fail); + __ cmp(scratch, Operand(TERMINAL_FAST_ELEMENTS_KIND)); + __ b(le, &done); __ cmp(scratch, Operand(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ b(lt, &fail); __ cmp(scratch, Operand(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); @@ -2788,9 +2790,11 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( (instr->additional_index() << shift_size))); } - __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); - __ cmp(scratch, Operand(kHoleNanUpper32)); - DeoptimizeIf(eq, instr->environment()); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); + __ cmp(scratch, Operand(kHoleNanUpper32)); + DeoptimizeIf(eq, instr->environment()); + } __ vldr(result, elements, 0); } @@ -2866,9 +2870,12 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3871,7 +3878,10 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3908,20 +3918,22 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ cmp(scratch, Operand(from_map)); __ b(ne, ¬_applicable); __ mov(new_map_reg, Operand(to_map)); - if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { __ str(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset)); // Write barrier. __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, scratch, kLRHasBeenSaved, kDontSaveFPRegs); - } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { + } else if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(r2)); ASSERT(new_map_reg.is(r3)); __ mov(fixed_object_reg, object_reg); CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), RelocInfo::CODE_TARGET, instr); - } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + } else if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(r2)); ASSERT(new_map_reg.is(r3)); @@ -4695,8 +4707,9 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { // Deopt if the array literal boilerplate ElementsKind is of a type different // than the expected one. The check isn't necessary if the boilerplate has - // already been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(r1, instr->hydrogen()->boilerplate_object()); // Load map into r2. __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); @@ -4847,10 +4860,11 @@ void LCodeGen::DoFastLiteral(LFastLiteral* instr) { ElementsKind boilerplate_elements_kind = instr->hydrogen()->boilerplate()->GetElementsKind(); - // Deopt if the literal boilerplate ElementsKind is of a type different than - // the expected one. The check isn't necessary if the boilerplate has already - // been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(r1, instr->hydrogen()->boilerplate()); // Load map into r2. __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 4da2fec..b4aec54 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -1868,10 +1868,12 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue)); b(hi, fail); } @@ -1879,22 +1881,25 @@ void MacroAssembler::CheckFastElements(Register map, void MacroAssembler::CheckFastObjectElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); b(ls, fail); - cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue)); b(hi, fail); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); b(hi, fail); } @@ -1997,22 +2002,17 @@ void MacroAssembler::CompareMap(Register obj, ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); cmp(scratch, Operand(map)); if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - b(eq, early_success); - cmp(scratch, Operand(Handle(transitioned_fast_element_map))); - } - - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - b(eq, early_success); - cmp(scratch, Operand(Handle(transitioned_double_map))); + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind, NULL); + if (!current_map) break; + b(eq, early_success); + cmp(scratch, Operand(Handle(current_map))); + } } } } @@ -2865,28 +2865,38 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - ldr(ip, MemOperand(scratch, Context::SlotOffset(expected_index))); - cmp(map_in_out, ip); + ldr(scratch, + MemOperand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + size_t offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + cmp(map_in_out, scratch); b(ne, no_map_match); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - ldr(map_in_out, MemOperand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + ldr(map_in_out, FieldMemOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; ldr(map_out, FieldMemOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 360f4c1..b93aba1 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -512,7 +512,8 @@ class MacroAssembler: public Assembler { // Load the initial map for new Arrays from a JSFunction. void LoadInitialArrayMap(Register function_in, Register scratch, - Register map_out); + Register map_out, + bool can_have_holes); void LoadGlobalFunction(int index, Register function); @@ -802,9 +803,9 @@ class MacroAssembler: public Assembler { // 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, - Register scratch, - Label* fail); + void CheckFastSmiElements(Register map, + Register scratch, + Label* fail); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by key in diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 49c0982..a024d79 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -1581,16 +1581,29 @@ Handle CallStubCompiler::CompileArrayPushCall( __ jmp(&fast_object); // In case of fast smi-only, convert to fast object, otherwise bail out. __ bind(¬_fast_object); - __ CheckFastSmiOnlyElements(r3, r7, &call_builtin); + __ CheckFastSmiElements(r3, r7, &call_builtin); // edx: receiver // r3: map - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + Label try_holey_map; + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, r3, r7, + &try_holey_map); + __ mov(r2, receiver); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + __ jmp(&fast_object); + + __ bind(&try_holey_map); + __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, + FAST_HOLEY_ELEMENTS, + r3, + r7, &call_builtin); __ mov(r2, receiver); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); __ bind(&fast_object); } else { __ CheckFastObjectElements(r3, r3, &call_builtin); @@ -3372,8 +3385,11 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3497,8 +3513,11 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( } break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3838,8 +3857,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3902,8 +3924,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -4042,8 +4067,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -4225,7 +4253,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( // Check that the key is a smi or a heap number convertible to a smi. GenerateSmiKeyCheck(masm, key_reg, r4, r5, d1, &miss_force_generic); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ JumpIfNotSmi(value_reg, &transition_elements_kind); } @@ -4253,7 +4281,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( DONT_DO_SMI_CHECK); __ bind(&finish_store); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ add(scratch, elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); @@ -4263,7 +4291,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); __ str(value_reg, MemOperand(scratch)); } else { - ASSERT(elements_kind == FAST_ELEMENTS); + ASSERT(IsFastObjectElementsKind(elements_kind)); __ add(scratch, elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 048a027..a20f87b 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1092,7 +1092,7 @@ bool Genesis::InitializeGlobal(Handle inner_global, // Check the state of the object. ASSERT(result->HasFastProperties()); - ASSERT(result->HasFastElements()); + ASSERT(result->HasFastObjectElements()); #endif } @@ -1185,7 +1185,7 @@ bool Genesis::InitializeGlobal(Handle inner_global, // Check the state of the object. ASSERT(result->HasFastProperties()); - ASSERT(result->HasFastElements()); + ASSERT(result->HasFastObjectElements()); #endif } @@ -1635,7 +1635,7 @@ bool Genesis::InstallNatives() { array_function->initial_map()->CopyDropTransitions(); Map* new_map; if (!maybe_map->To(&new_map)) return false; - new_map->set_elements_kind(FAST_ELEMENTS); + new_map->set_elements_kind(FAST_HOLEY_ELEMENTS); array_function->set_initial_map(new_map); // Make "length" magic on instances. diff --git a/src/builtins.cc b/src/builtins.cc index 84a0c3d..64ec3d9 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -200,9 +200,12 @@ static MaybeObject* ArrayCodeGenericCommon(Arguments* args, array->set_elements(heap->empty_fixed_array()); if (!FLAG_smi_only_arrays) { Context* global_context = isolate->context()->global_context(); - if (array->GetElementsKind() == FAST_SMI_ONLY_ELEMENTS && - !global_context->object_js_array_map()->IsUndefined()) { - array->set_map(Map::cast(global_context->object_js_array_map())); + if (array->GetElementsKind() == GetInitialFastElementsKind() && + !global_context->js_array_maps()->IsUndefined()) { + FixedArray* map_array = + FixedArray::cast(global_context->js_array_maps()); + array->set_map(Map::cast(map_array-> + get(TERMINAL_FAST_ELEMENTS_KIND))); } } } else { @@ -222,6 +225,13 @@ static MaybeObject* ArrayCodeGenericCommon(Arguments* args, { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len); if (!maybe_obj->ToObject(&fixed_array)) return maybe_obj; } + ElementsKind elements_kind = array->GetElementsKind(); + if (!IsFastHoleyElementsKind(elements_kind)) { + elements_kind = GetHoleyElementsKind(elements_kind); + MaybeObject* maybe_array = + array->TransitionElementsKind(elements_kind); + if (maybe_array->IsFailure()) return maybe_array; + } // We do not use SetContent to skip the unnecessary elements type check. array->set_elements(FixedArray::cast(fixed_array)); array->set_length(Smi::cast(obj)); @@ -250,7 +260,7 @@ static MaybeObject* ArrayCodeGenericCommon(Arguments* args, // Allocate an appropriately typed elements array. MaybeObject* maybe_elms; ElementsKind elements_kind = array->GetElementsKind(); - if (elements_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastDoubleElementsKind(elements_kind)) { maybe_elms = heap->AllocateUninitializedFixedDoubleArray( number_of_elements); } else { @@ -261,13 +271,15 @@ static MaybeObject* ArrayCodeGenericCommon(Arguments* args, // Fill in the content switch (array->GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: { + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_SMI_ELEMENTS: { FixedArray* smi_elms = FixedArray::cast(elms); for (int index = 0; index < number_of_elements; index++) { smi_elms->set(index, (*args)[index+1], SKIP_WRITE_BARRIER); } break; } + case FAST_HOLEY_ELEMENTS: case FAST_ELEMENTS: { AssertNoAllocation no_gc; WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); @@ -277,6 +289,7 @@ static MaybeObject* ArrayCodeGenericCommon(Arguments* args, } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { FixedDoubleArray* double_elms = FixedDoubleArray::cast(elms); for (int index = 0; index < number_of_elements; index++) { @@ -412,7 +425,7 @@ static inline MaybeObject* EnsureJSArrayWithWritableFastElements( HeapObject* elms = array->elements(); Map* map = elms->map(); if (map == heap->fixed_array_map()) { - if (args == NULL || array->HasFastElements()) return elms; + if (args == NULL || array->HasFastObjectElements()) return elms; if (array->HasFastDoubleElements()) { ASSERT(elms == heap->empty_fixed_array()); MaybeObject* maybe_transition = @@ -422,7 +435,7 @@ static inline MaybeObject* EnsureJSArrayWithWritableFastElements( } } else if (map == heap->fixed_cow_array_map()) { MaybeObject* maybe_writable_result = array->EnsureWritableFastElements(); - if (args == NULL || array->HasFastElements() || + if (args == NULL || array->HasFastObjectElements() || maybe_writable_result->IsFailure()) { return maybe_writable_result; } @@ -516,8 +529,8 @@ BUILTIN(ArrayPush) { } FixedArray* new_elms = FixedArray::cast(obj); - CopyObjectToObjectElements(elms, FAST_ELEMENTS, 0, - new_elms, FAST_ELEMENTS, 0, len); + ElementsKind kind = array->GetElementsKind(); + CopyObjectToObjectElements(elms, kind, 0, new_elms, kind, 0, len); FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; @@ -588,7 +601,7 @@ BUILTIN(ArrayShift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastTypeElements()); + ASSERT(array->HasFastSmiOrObjectElements()); int len = Smi::cast(array->length())->value(); if (len == 0) return heap->undefined_value(); @@ -630,7 +643,7 @@ BUILTIN(ArrayUnshift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastTypeElements()); + ASSERT(array->HasFastSmiOrObjectElements()); int len = Smi::cast(array->length())->value(); int to_add = args.length() - 1; @@ -652,8 +665,8 @@ BUILTIN(ArrayUnshift) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* new_elms = FixedArray::cast(obj); - CopyObjectToObjectElements(elms, FAST_ELEMENTS, 0, - new_elms, FAST_ELEMENTS, to_add, len); + ElementsKind kind = array->GetElementsKind(); + CopyObjectToObjectElements(elms, kind, 0, new_elms, kind, to_add, len); FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; array->set_elements(elms); @@ -682,7 +695,7 @@ BUILTIN(ArraySlice) { int len = -1; if (receiver->IsJSArray()) { JSArray* array = JSArray::cast(receiver); - if (!array->HasFastTypeElements() || + if (!array->HasFastSmiOrObjectElements() || !IsJSArrayFastElementMovingAllowed(heap, array)) { return CallJsBuiltin(isolate, "ArraySlice", args); } @@ -698,7 +711,7 @@ BUILTIN(ArraySlice) { bool is_arguments_object_with_fast_elements = receiver->IsJSObject() && JSObject::cast(receiver)->map() == arguments_map - && JSObject::cast(receiver)->HasFastTypeElements(); + && JSObject::cast(receiver)->HasFastSmiOrObjectElements(); if (!is_arguments_object_with_fast_elements) { return CallJsBuiltin(isolate, "ArraySlice", args); } @@ -763,9 +776,9 @@ BUILTIN(ArraySlice) { JSArray* result_array; if (!maybe_array->To(&result_array)) return maybe_array; - CopyObjectToObjectElements(elms, FAST_ELEMENTS, k, + CopyObjectToObjectElements(elms, elements_kind, k, FixedArray::cast(result_array->elements()), - FAST_ELEMENTS, 0, result_len); + elements_kind, 0, result_len); return result_array; } @@ -786,7 +799,7 @@ BUILTIN(ArraySplice) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastTypeElements()); + ASSERT(array->HasFastSmiOrObjectElements()); int len = Smi::cast(array->length())->value(); @@ -837,9 +850,9 @@ BUILTIN(ArraySplice) { { // Fill newly created array. - CopyObjectToObjectElements(elms, FAST_ELEMENTS, actual_start, + CopyObjectToObjectElements(elms, elements_kind, actual_start, FixedArray::cast(result_array->elements()), - FAST_ELEMENTS, 0, actual_delete_count); + elements_kind, 0, actual_delete_count); } int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0; @@ -888,12 +901,13 @@ BUILTIN(ArraySplice) { { // Copy the part before actual_start as is. - CopyObjectToObjectElements(elms, FAST_ELEMENTS, 0, - new_elms, FAST_ELEMENTS, 0, actual_start); + ElementsKind kind = array->GetElementsKind(); + CopyObjectToObjectElements(elms, kind, 0, + new_elms, kind, 0, actual_start); const int to_copy = len - actual_delete_count - actual_start; - CopyObjectToObjectElements(elms, FAST_ELEMENTS, + CopyObjectToObjectElements(elms, kind, actual_start + actual_delete_count, - new_elms, FAST_ELEMENTS, + new_elms, kind, actual_start + item_count, to_copy); } @@ -940,11 +954,12 @@ BUILTIN(ArrayConcat) { // and calculating total length. int n_arguments = args.length(); int result_len = 0; - ElementsKind elements_kind = FAST_SMI_ONLY_ELEMENTS; + ElementsKind elements_kind = GetInitialFastElementsKind(); for (int i = 0; i < n_arguments; i++) { Object* arg = args[i]; - if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastTypeElements() - || JSArray::cast(arg)->GetPrototype() != array_proto) { + if (!arg->IsJSArray() || + !JSArray::cast(arg)->HasFastSmiOrObjectElements() || + JSArray::cast(arg)->GetPrototype() != array_proto) { return CallJsBuiltin(isolate, "ArrayConcat", args); } @@ -961,8 +976,18 @@ BUILTIN(ArrayConcat) { return CallJsBuiltin(isolate, "ArrayConcat", args); } - if (!JSArray::cast(arg)->HasFastSmiOnlyElements()) { - elements_kind = FAST_ELEMENTS; + if (!JSArray::cast(arg)->HasFastSmiElements()) { + if (IsFastSmiElementsKind(elements_kind)) { + if (IsFastHoleyElementsKind(elements_kind)) { + elements_kind = FAST_HOLEY_ELEMENTS; + } else { + elements_kind = FAST_ELEMENTS; + } + } + } + + if (JSArray::cast(arg)->HasFastHoleyElements()) { + elements_kind = GetHoleyElementsKind(elements_kind); } } @@ -982,8 +1007,8 @@ BUILTIN(ArrayConcat) { JSArray* array = JSArray::cast(args[i]); int len = Smi::cast(array->length())->value(); FixedArray* elms = FixedArray::cast(array->elements()); - CopyObjectToObjectElements(elms, FAST_ELEMENTS, 0, - result_elms, FAST_ELEMENTS, + CopyObjectToObjectElements(elms, elements_kind, 0, + result_elms, elements_kind, start_pos, len); start_pos += len; } diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 814e358..8f31660 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -262,10 +262,13 @@ void JSEntryStub::FinishCode(Handle code) { void KeyedLoadElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastElement(masm); break; case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(masm); break; case EXTERNAL_BYTE_ELEMENTS: @@ -292,7 +295,9 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) { void KeyedStoreElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: { + case FAST_HOLEY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: { KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_, elements_kind_, @@ -300,6 +305,7 @@ void KeyedStoreElementStub::Generate(MacroAssembler* masm) { } break; case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, is_js_array_, grow_mode_); @@ -430,24 +436,32 @@ bool ToBooleanStub::Types::CanBeUndetectable() const { void ElementsTransitionAndStoreStub::Generate(MacroAssembler* masm) { Label fail; + ASSERT(!IsFastHoleyElementsKind(from_) || IsFastHoleyElementsKind(to_)); if (!FLAG_trace_elements_transitions) { - if (to_ == FAST_ELEMENTS) { - if (from_ == FAST_SMI_ONLY_ELEMENTS) { - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); - } else if (from_ == FAST_DOUBLE_ELEMENTS) { + if (IsFastSmiOrObjectElementsKind(to_)) { + if (IsFastSmiOrObjectElementsKind(from_)) { + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm); + } else if (IsFastDoubleElementsKind(from_)) { + ASSERT(!IsFastSmiElementsKind(to_)); ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); } else { UNREACHABLE(); } KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_jsarray_, - FAST_ELEMENTS, + to_, grow_mode_); - } else if (from_ == FAST_SMI_ONLY_ELEMENTS && to_ == FAST_DOUBLE_ELEMENTS) { - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + } else if (IsFastSmiElementsKind(from_) && + IsFastDoubleElementsKind(to_)) { + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, is_jsarray_, grow_mode_); + } else if (IsFastDoubleElementsKind(from_)) { + ASSERT(to_ == FAST_HOLEY_DOUBLE_ELEMENTS); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm); } else { UNREACHABLE(); } diff --git a/src/codegen.h b/src/codegen.h index 50d70f2..08a777f 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -95,8 +95,8 @@ UnaryMathFunction CreateSqrtFunction(); class ElementsTransitionGenerator : public AllStatic { public: - static void GenerateSmiOnlyToObject(MacroAssembler* masm); - static void GenerateSmiOnlyToDouble(MacroAssembler* masm, Label* fail); + static void GenerateMapChangeElementsTransition(MacroAssembler* masm); + static void GenerateSmiToDouble(MacroAssembler* masm, Label* fail); static void GenerateDoubleToObject(MacroAssembler* masm, Label* fail); private: diff --git a/src/contexts.h b/src/contexts.h index 647c15c..d154b82 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -106,9 +106,7 @@ enum BindingFlags { V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \ V(INTERNAL_ARRAY_FUNCTION_INDEX, JSFunction, internal_array_function) \ V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \ - V(SMI_JS_ARRAY_MAP_INDEX, Object, smi_js_array_map) \ - V(DOUBLE_JS_ARRAY_MAP_INDEX, Object, double_js_array_map) \ - V(OBJECT_JS_ARRAY_MAP_INDEX, Object, object_js_array_map) \ + V(JS_ARRAY_MAPS_INDEX, Object, js_array_maps) \ V(DATE_FUNCTION_INDEX, JSFunction, date_function) \ V(JSON_OBJECT_INDEX, JSObject, json_object) \ V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \ @@ -248,9 +246,7 @@ class Context: public FixedArray { OBJECT_FUNCTION_INDEX, INTERNAL_ARRAY_FUNCTION_INDEX, ARRAY_FUNCTION_INDEX, - SMI_JS_ARRAY_MAP_INDEX, - DOUBLE_JS_ARRAY_MAP_INDEX, - OBJECT_JS_ARRAY_MAP_INDEX, + JS_ARRAY_MAPS_INDEX, DATE_FUNCTION_INDEX, JSON_OBJECT_INDEX, REGEXP_FUNCTION_INDEX, @@ -373,18 +369,6 @@ class Context: public FixedArray { Object* OptimizedFunctionsListHead(); void ClearOptimizedFunctions(); - static int GetContextMapIndexFromElementsKind( - ElementsKind elements_kind) { - if (elements_kind == FAST_DOUBLE_ELEMENTS) { - return Context::DOUBLE_JS_ARRAY_MAP_INDEX; - } else if (elements_kind == FAST_ELEMENTS) { - return Context::OBJECT_JS_ARRAY_MAP_INDEX; - } else { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); - return Context::SMI_JS_ARRAY_MAP_INDEX; - } - } - #define GLOBAL_CONTEXT_FIELD_ACCESSORS(index, type, name) \ void set_##name(type* value) { \ ASSERT(IsGlobalContext()); \ diff --git a/src/elements-kind.cc b/src/elements-kind.cc new file mode 100644 index 0000000..655a23b --- /dev/null +++ b/src/elements-kind.cc @@ -0,0 +1,134 @@ +// Copyright 2012 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. + +#include "elements-kind.h" + +#include "api.h" +#include "elements.h" +#include "objects.h" + +namespace v8 { +namespace internal { + + +void PrintElementsKind(FILE* out, ElementsKind kind) { + ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); + PrintF(out, "%s", accessor->name()); +} + + +ElementsKind GetInitialFastElementsKind() { + if (FLAG_packed_arrays) { + return FAST_SMI_ELEMENTS; + } else { + return FAST_HOLEY_SMI_ELEMENTS; + } +} + + +struct InitializeFastElementsKindSequence { + static void Construct( + ElementsKind** fast_elements_kind_sequence_ptr) { + ElementsKind* fast_elements_kind_sequence = + new ElementsKind[kFastElementsKindCount]; + *fast_elements_kind_sequence_ptr = fast_elements_kind_sequence; + STATIC_ASSERT(FAST_SMI_ELEMENTS == FIRST_FAST_ELEMENTS_KIND); + fast_elements_kind_sequence[0] = FAST_SMI_ELEMENTS; + fast_elements_kind_sequence[1] = FAST_HOLEY_SMI_ELEMENTS; + fast_elements_kind_sequence[2] = FAST_DOUBLE_ELEMENTS; + fast_elements_kind_sequence[3] = FAST_HOLEY_DOUBLE_ELEMENTS; + fast_elements_kind_sequence[4] = FAST_ELEMENTS; + fast_elements_kind_sequence[5] = FAST_HOLEY_ELEMENTS; + } +}; + + +static LazyInstance::type + fast_elements_kind_sequence = LAZY_INSTANCE_INITIALIZER; + + +ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_number) { + ASSERT(sequence_number >= 0 && + sequence_number < kFastElementsKindCount); + return fast_elements_kind_sequence.Get()[sequence_number]; +} + +int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind) { + for (int i = 0; i < kFastElementsKindCount; ++i) { + if (fast_elements_kind_sequence.Get()[i] == elements_kind) { + return i; + } + } + UNREACHABLE(); + return 0; +} + + +ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind, + bool allow_only_packed) { + ASSERT(IsFastElementsKind(elements_kind)); + ASSERT(elements_kind != TERMINAL_FAST_ELEMENTS_KIND); + while (true) { + int index = + GetSequenceIndexFromFastElementsKind(elements_kind) + 1; + elements_kind = GetFastElementsKindFromSequenceIndex(index); + if (!IsFastHoleyElementsKind(elements_kind) || !allow_only_packed) { + return elements_kind; + } + } + UNREACHABLE(); + return TERMINAL_FAST_ELEMENTS_KIND; +} + + +bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, + ElementsKind to_kind) { + switch (from_kind) { + case FAST_SMI_ELEMENTS: + return to_kind != FAST_SMI_ELEMENTS; + case FAST_HOLEY_SMI_ELEMENTS: + return to_kind != FAST_SMI_ELEMENTS && + to_kind != FAST_HOLEY_SMI_ELEMENTS; + case FAST_DOUBLE_ELEMENTS: + return to_kind != FAST_SMI_ELEMENTS && + to_kind != FAST_HOLEY_SMI_ELEMENTS && + to_kind != FAST_DOUBLE_ELEMENTS; + case FAST_HOLEY_DOUBLE_ELEMENTS: + return to_kind == FAST_ELEMENTS || + to_kind == FAST_HOLEY_ELEMENTS; + case FAST_ELEMENTS: + return to_kind == FAST_HOLEY_ELEMENTS; + case FAST_HOLEY_ELEMENTS: + return false; + default: + return false; + } +} + + +} } // namespace v8::internal diff --git a/src/elements-kind.h b/src/elements-kind.h new file mode 100644 index 0000000..47a146d --- /dev/null +++ b/src/elements-kind.h @@ -0,0 +1,209 @@ +// Copyright 2012 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. + +#ifndef V8_ELEMENTS_KIND_H_ +#define V8_ELEMENTS_KIND_H_ + +#include "v8checks.h" + +namespace v8 { +namespace internal { + +enum ElementsKind { + // 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_ELEMENTS, + FAST_HOLEY_SMI_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, + FAST_HOLEY_ELEMENTS, + + // The "fast" kind for unwrapped, non-tagged double values. + FAST_DOUBLE_ELEMENTS, + FAST_HOLEY_DOUBLE_ELEMENTS, + + // The "slow" kind. + DICTIONARY_ELEMENTS, + NON_STRICT_ARGUMENTS_ELEMENTS, + // The "fast" kind for external arrays + EXTERNAL_BYTE_ELEMENTS, + EXTERNAL_UNSIGNED_BYTE_ELEMENTS, + EXTERNAL_SHORT_ELEMENTS, + EXTERNAL_UNSIGNED_SHORT_ELEMENTS, + EXTERNAL_INT_ELEMENTS, + EXTERNAL_UNSIGNED_INT_ELEMENTS, + EXTERNAL_FLOAT_ELEMENTS, + EXTERNAL_DOUBLE_ELEMENTS, + EXTERNAL_PIXEL_ELEMENTS, + + // Derived constants from ElementsKind + FIRST_ELEMENTS_KIND = FAST_SMI_ELEMENTS, + LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, + FIRST_FAST_ELEMENTS_KIND = FAST_SMI_ELEMENTS, + LAST_FAST_ELEMENTS_KIND = FAST_HOLEY_DOUBLE_ELEMENTS, + FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, + LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, + TERMINAL_FAST_ELEMENTS_KIND = FAST_HOLEY_ELEMENTS +}; + +const int kElementsKindCount = LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; +const int kFastElementsKindCount = LAST_FAST_ELEMENTS_KIND - + FIRST_FAST_ELEMENTS_KIND + 1; + +void PrintElementsKind(FILE* out, ElementsKind kind); + +ElementsKind GetInitialFastElementsKind(); + +ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_index); + +int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind); + + +inline bool IsFastElementsKind(ElementsKind kind) { + ASSERT(FIRST_FAST_ELEMENTS_KIND == 0); + return kind <= FAST_HOLEY_DOUBLE_ELEMENTS; +} + + +inline bool IsFastDoubleElementsKind(ElementsKind kind) { + return kind == FAST_DOUBLE_ELEMENTS || + kind == FAST_HOLEY_DOUBLE_ELEMENTS; +} + + +inline bool IsFastSmiOrObjectElementsKind(ElementsKind kind) { + return kind == FAST_SMI_ELEMENTS || + kind == FAST_HOLEY_SMI_ELEMENTS || + kind == FAST_ELEMENTS || + kind == FAST_HOLEY_ELEMENTS; +} + + +inline bool IsFastSmiElementsKind(ElementsKind kind) { + return kind == FAST_SMI_ELEMENTS || + kind == FAST_HOLEY_SMI_ELEMENTS; +} + + +inline bool IsFastObjectElementsKind(ElementsKind kind) { + return kind == FAST_ELEMENTS || + kind == FAST_HOLEY_ELEMENTS; +} + + +inline bool IsFastHoleyElementsKind(ElementsKind kind) { + return kind == FAST_HOLEY_SMI_ELEMENTS || + kind == FAST_HOLEY_DOUBLE_ELEMENTS || + kind == FAST_HOLEY_ELEMENTS; +} + + +inline bool IsHoleyElementsKind(ElementsKind kind) { + return IsFastHoleyElementsKind(kind) || + kind == DICTIONARY_ELEMENTS; +} + + +inline bool IsFastPackedElementsKind(ElementsKind kind) { + return kind == FAST_SMI_ELEMENTS || + kind == FAST_DOUBLE_ELEMENTS || + kind == FAST_ELEMENTS; +} + + +inline ElementsKind GetPackedElementsKind(ElementsKind holey_kind) { + if (holey_kind == FAST_HOLEY_SMI_ELEMENTS) { + return FAST_SMI_ELEMENTS; + } + if (holey_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { + return FAST_DOUBLE_ELEMENTS; + } + if (holey_kind == FAST_HOLEY_ELEMENTS) { + return FAST_ELEMENTS; + } + return holey_kind; +} + + +inline ElementsKind GetHoleyElementsKind(ElementsKind packed_kind) { + if (packed_kind == FAST_SMI_ELEMENTS) { + return FAST_HOLEY_SMI_ELEMENTS; + } + if (packed_kind == FAST_DOUBLE_ELEMENTS) { + return FAST_HOLEY_DOUBLE_ELEMENTS; + } + if (packed_kind == FAST_ELEMENTS) { + return FAST_HOLEY_ELEMENTS; + } + return packed_kind; +} + + +inline ElementsKind FastSmiToObjectElementsKind(ElementsKind from_kind) { + ASSERT(IsFastSmiElementsKind(from_kind)); + return (from_kind == FAST_SMI_ELEMENTS) + ? FAST_ELEMENTS + : FAST_HOLEY_ELEMENTS; +} + + +inline bool IsSimpleMapChangeTransition(ElementsKind from_kind, + ElementsKind to_kind) { + return (GetHoleyElementsKind(from_kind) == to_kind) || + (IsFastSmiElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)); +} + + +bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, + ElementsKind to_kind); + + +inline bool IsTransitionableFastElementsKind(ElementsKind from_kind) { + return IsFastElementsKind(from_kind) && + from_kind != TERMINAL_FAST_ELEMENTS_KIND; +} + + +ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind, + bool allow_only_packed); + + +inline bool CanTransitionToMoreGeneralFastElementsKind( + ElementsKind elements_kind, + bool allow_only_packed) { + return elements_kind != TERMINAL_FAST_ELEMENTS_KIND && + (!allow_only_packed || elements_kind != FAST_ELEMENTS); +} + + +} } // namespace v8::internal + +#endif // V8_ELEMENTS_KIND_H_ diff --git a/src/elements.cc b/src/elements.cc index d367af8..2692cb5 100644 --- a/src/elements.cc +++ b/src/elements.cc @@ -39,8 +39,14 @@ // Inheritance hierarchy: // - ElementsAccessorBase (abstract) // - FastElementsAccessor (abstract) -// - FastObjectElementsAccessor +// - FastSmiOrObjectElementsAccessor +// - FastPackedSmiElementsAccessor +// - FastHoleySmiElementsAccessor +// - FastPackedObjectElementsAccessor +// - FastHoleyObjectElementsAccessor // - FastDoubleElementsAccessor +// - FastPackedDoubleElementsAccessor +// - FastHoleyDoubleElementsAccessor // - ExternalElementsAccessor (abstract) // - ExternalByteElementsAccessor // - ExternalUnsignedByteElementsAccessor @@ -65,9 +71,15 @@ namespace internal { // identical. Note that the order must match that of the ElementsKind enum for // the |accessor_array[]| below to work. #define ELEMENTS_LIST(V) \ - V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS, FixedArray) \ - V(FastObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \ - V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, FixedDoubleArray) \ + V(FastPackedSmiElementsAccessor, FAST_SMI_ELEMENTS, FixedArray) \ + V(FastHoleySmiElementsAccessor, FAST_HOLEY_SMI_ELEMENTS, \ + FixedArray) \ + V(FastPackedObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \ + V(FastHoleyObjectElementsAccessor, FAST_HOLEY_ELEMENTS, FixedArray) \ + V(FastPackedDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, \ + FixedDoubleArray) \ + V(FastHoleyDoubleElementsAccessor, FAST_HOLEY_DOUBLE_ELEMENTS, \ + FixedDoubleArray) \ V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, \ SeededNumberDictionary) \ V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS, \ @@ -139,8 +151,6 @@ void CopyObjectToObjectElements(FixedArray* from, uint32_t to_start, int raw_copy_size) { ASSERT(to->map() != HEAP->fixed_cow_array_map()); - ASSERT(from_kind == FAST_ELEMENTS || from_kind == FAST_SMI_ONLY_ELEMENTS); - ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS); int copy_size = raw_copy_size; if (raw_copy_size < 0) { ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || @@ -148,7 +158,7 @@ void CopyObjectToObjectElements(FixedArray* from, copy_size = Min(from->length() - from_start, to->length() - to_start); #ifdef DEBUG - // FAST_ELEMENT arrays cannot be uninitialized. Ensure they are already + // FAST_*_ELEMENTS arrays cannot be uninitialized. Ensure they are already // marked with the hole. if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { for (int i = to_start + copy_size; i < to->length(); ++i) { @@ -160,12 +170,15 @@ void CopyObjectToObjectElements(FixedArray* from, ASSERT((copy_size + static_cast(to_start)) <= to->length() && (copy_size + static_cast(from_start)) <= from->length()); if (copy_size == 0) return; + ASSERT(IsFastSmiOrObjectElementsKind(from_kind)); + ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); Address to_address = to->address() + FixedArray::kHeaderSize; Address from_address = from->address() + FixedArray::kHeaderSize; CopyWords(reinterpret_cast(to_address) + to_start, reinterpret_cast(from_address) + from_start, copy_size); - if (from_kind == FAST_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsFastObjectElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { Heap* heap = from->GetHeap(); if (!heap->InNewSpace(to)) { heap->RecordWrites(to->address(), @@ -190,7 +203,7 @@ static void CopyDictionaryToObjectElements(SeededNumberDictionary* from, raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); copy_size = from->max_number_key() + 1 - from_start; #ifdef DEBUG - // FAST_ELEMENT arrays cannot be uninitialized. Ensure they are already + // Fast object arrays cannot be uninitialized. Ensure they are already // marked with the hole. if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { for (int i = to_start + copy_size; i < to->length(); ++i) { @@ -200,7 +213,7 @@ static void CopyDictionaryToObjectElements(SeededNumberDictionary* from, #endif } ASSERT(to != from); - ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS); + ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); if (copy_size == 0) return; uint32_t to_length = to->length(); if (to_start + copy_size > to_length) { @@ -216,7 +229,7 @@ static void CopyDictionaryToObjectElements(SeededNumberDictionary* from, to->set_the_hole(i + to_start); } } - if (to_kind == FAST_ELEMENTS) { + if (IsFastObjectElementsKind(to_kind)) { if (!heap->InNewSpace(to)) { heap->RecordWrites(to->address(), to->OffsetOfElementAt(to_start), @@ -234,7 +247,7 @@ MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements( ElementsKind to_kind, uint32_t to_start, int raw_copy_size) { - ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS); + ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); int copy_size = raw_copy_size; if (raw_copy_size < 0) { ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || @@ -242,7 +255,7 @@ MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements( copy_size = Min(from->length() - from_start, to->length() - to_start); #ifdef DEBUG - // FAST_ELEMENT arrays cannot be uninitialized. Ensure they are already + // FAST_*_ELEMENTS arrays cannot be uninitialized. Ensure they are already // marked with the hole. if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { for (int i = to_start + copy_size; i < to->length(); ++i) { @@ -255,14 +268,14 @@ MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements( (copy_size + static_cast(from_start)) <= from->length()); if (copy_size == 0) return from; for (int i = 0; i < copy_size; ++i) { - if (to_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(to_kind)) { UNIMPLEMENTED(); return Failure::Exception(); } else { MaybeObject* maybe_value = from->get(i + from_start); Object* value; - ASSERT(to_kind == FAST_ELEMENTS); - // Because FAST_DOUBLE_ELEMENTS -> FAST_ELEMENT allocate HeapObjects + ASSERT(IsFastObjectElementsKind(to_kind)); + // Because Double -> Object elements transitions allocate HeapObjects // iteratively, the allocate must succeed within a single GC cycle, // otherwise the retry after the GC will also fail. In order to ensure // that no GC is triggered, allocate HeapNumbers from old space if they @@ -404,6 +417,38 @@ class ElementsAccessorBase : public ElementsAccessor { virtual ElementsKind kind() const { return ElementsTraits::Kind; } + static void ValidateContents(JSObject* holder, int length) { + } + + static void ValidateImpl(JSObject* holder) { + FixedArrayBase* fixed_array_base = holder->elements(); + // When objects are first allocated, its elements are Failures. + if (fixed_array_base->IsFailure()) return; + if (!fixed_array_base->IsHeapObject()) return; + Map* map = fixed_array_base->map(); + // Arrays that have been shifted in place can't be verified. + Heap* heap = holder->GetHeap(); + if (map == heap->raw_unchecked_one_pointer_filler_map() || + map == heap->raw_unchecked_two_pointer_filler_map() || + map == heap->free_space_map()) { + return; + } + int length = 0; + if (holder->IsJSArray()) { + Object* length_obj = JSArray::cast(holder)->length(); + if (length_obj->IsSmi()) { + length = Smi::cast(length_obj)->value(); + } + } else { + length = fixed_array_base->length(); + } + ElementsAccessorSubclass::ValidateContents(holder, length); + } + + virtual void Validate(JSObject* holder) { + ElementsAccessorSubclass::ValidateImpl(holder); + } + static bool HasElementImpl(Object* receiver, JSObject* holder, uint32_t key, @@ -455,9 +500,10 @@ class ElementsAccessorBase : public ElementsAccessor { Object* length, BackingStore* backing_store); - MUST_USE_RESULT virtual MaybeObject* SetCapacityAndLength(JSArray* array, - int capacity, - int length) { + MUST_USE_RESULT virtual MaybeObject* SetCapacityAndLength( + JSArray* array, + int capacity, + int length) { return ElementsAccessorSubclass::SetFastElementsCapacityAndLength( array, capacity, @@ -623,6 +669,7 @@ class FastElementsAccessor KindTraits>(name) {} protected: friend class ElementsAccessorBase; + friend class NonStrictArgumentsElementsAccessor; typedef typename KindTraits::BackingStore BackingStore; @@ -633,10 +680,21 @@ class FastElementsAccessor Object* length_object, uint32_t length) { uint32_t old_capacity = backing_store->length(); + Object* old_length = array->length(); + bool same_size = old_length->IsSmi() && + static_cast(Smi::cast(old_length)->value()) == length; + ElementsKind kind = array->GetElementsKind(); + + if (!same_size && IsFastElementsKind(kind) && + !IsFastHoleyElementsKind(kind)) { + kind = GetHoleyElementsKind(kind); + MaybeObject* maybe_obj = array->TransitionElementsKind(kind); + if (maybe_obj->IsFailure()) return maybe_obj; + } // Check whether the backing store should be shrunk. if (length <= old_capacity) { - if (array->HasFastTypeElements()) { + if (array->HasFastSmiOrObjectElements()) { MaybeObject* maybe_obj = array->EnsureWritableFastElements(); if (!maybe_obj->To(&backing_store)) return maybe_obj; } @@ -668,39 +726,40 @@ class FastElementsAccessor MaybeObject* result = FastElementsAccessorSubclass:: SetFastElementsCapacityAndLength(array, new_capacity, length); if (result->IsFailure()) return result; + array->ValidateElements(); return length_object; } // Request conversion to slow elements. return array->GetHeap()->undefined_value(); } -}; - - -class FastObjectElementsAccessor - : public FastElementsAccessor, - kPointerSize> { - public: - explicit FastObjectElementsAccessor(const char* name) - : FastElementsAccessor, - kPointerSize>(name) {} static MaybeObject* DeleteCommon(JSObject* obj, - uint32_t key) { - ASSERT(obj->HasFastElements() || - obj->HasFastSmiOnlyElements() || + uint32_t key, + JSReceiver::DeleteMode mode) { + ASSERT(obj->HasFastSmiOrObjectElements() || + obj->HasFastDoubleElements() || obj->HasFastArgumentsElements()); + typename KindTraits::BackingStore* backing_store = + KindTraits::BackingStore::cast(obj->elements()); Heap* heap = obj->GetHeap(); - FixedArray* backing_store = FixedArray::cast(obj->elements()); if (backing_store->map() == heap->non_strict_arguments_elements_map()) { - backing_store = FixedArray::cast(backing_store->get(1)); + backing_store = + KindTraits::BackingStore::cast( + FixedArray::cast(backing_store)->get(1)); } else { - Object* writable; - MaybeObject* maybe = obj->EnsureWritableFastElements(); - if (!maybe->ToObject(&writable)) return maybe; - backing_store = FixedArray::cast(writable); + ElementsKind kind = KindTraits::Kind; + if (IsFastPackedElementsKind(kind)) { + MaybeObject* transitioned = + obj->TransitionElementsKind(GetHoleyElementsKind(kind)); + if (transitioned->IsFailure()) return transitioned; + } + if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) { + Object* writable; + MaybeObject* maybe = obj->EnsureWritableFastElements(); + if (!maybe->ToObject(&writable)) return maybe; + backing_store = KindTraits::BackingStore::cast(writable); + } } uint32_t length = static_cast( obj->IsJSArray() @@ -712,15 +771,14 @@ class FastObjectElementsAccessor // has too few used values, normalize it. // To avoid doing the check on every delete we require at least // one adjacent hole to the value being deleted. - Object* hole = heap->the_hole_value(); const int kMinLengthForSparsenessCheck = 64; if (backing_store->length() >= kMinLengthForSparsenessCheck && !heap->InNewSpace(backing_store) && - ((key > 0 && backing_store->get(key - 1) == hole) || - (key + 1 < length && backing_store->get(key + 1) == hole))) { + ((key > 0 && backing_store->is_the_hole(key - 1)) || + (key + 1 < length && backing_store->is_the_hole(key + 1)))) { int num_used = 0; for (int i = 0; i < backing_store->length(); ++i) { - if (backing_store->get(i) != hole) ++num_used; + if (!backing_store->is_the_hole(i)) ++num_used; // Bail out early if more than 1/4 is used. if (4 * num_used > backing_store->length()) break; } @@ -733,27 +791,75 @@ class FastObjectElementsAccessor return heap->true_value(); } + virtual MaybeObject* Delete(JSObject* obj, + uint32_t key, + JSReceiver::DeleteMode mode) { + return DeleteCommon(obj, key, mode); + } + + static bool HasElementImpl( + Object* receiver, + JSObject* holder, + uint32_t key, + typename KindTraits::BackingStore* backing_store) { + if (key >= static_cast(backing_store->length())) { + return false; + } + return !backing_store->is_the_hole(key); + } + + static void ValidateContents(JSObject* holder, int length) { +#if DEBUG + FixedArrayBase* elements = holder->elements(); + Heap* heap = elements->GetHeap(); + Map* map = elements->map(); + ASSERT((IsFastSmiOrObjectElementsKind(KindTraits::Kind) && + (map == heap->fixed_array_map() || + map == heap->fixed_cow_array_map())) || + (IsFastDoubleElementsKind(KindTraits::Kind) == + ((map == heap->fixed_array_map() && length == 0) || + map == heap->fixed_double_array_map()))); + for (int i = 0; i < length; i++) { + typename KindTraits::BackingStore* backing_store = + KindTraits::BackingStore::cast(elements); + ASSERT((!IsFastSmiElementsKind(KindTraits::Kind) || + static_cast(backing_store->get(i))->IsSmi()) || + (IsFastHoleyElementsKind(KindTraits::Kind) == + backing_store->is_the_hole(i))); + } +#endif + } +}; + + +template +class FastSmiOrObjectElementsAccessor + : public FastElementsAccessor { + public: + explicit FastSmiOrObjectElementsAccessor(const char* name) + : FastElementsAccessor(name) {} + static MaybeObject* CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, FixedArrayBase* to, ElementsKind to_kind, uint32_t to_start, int copy_size) { - switch (to_kind) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { - CopyObjectToObjectElements( - FixedArray::cast(from), ElementsTraits::Kind, from_start, - FixedArray::cast(to), to_kind, to_start, copy_size); - return from; - } - case FAST_DOUBLE_ELEMENTS: - CopyObjectToDoubleElements( - FixedArray::cast(from), from_start, - FixedDoubleArray::cast(to), to_start, copy_size); - return from; - default: - UNREACHABLE(); + if (IsFastSmiOrObjectElementsKind(to_kind)) { + CopyObjectToObjectElements( + FixedArray::cast(from), KindTraits::Kind, from_start, + FixedArray::cast(to), to_kind, to_start, copy_size); + } else if (IsFastDoubleElementsKind(to_kind)) { + CopyObjectToDoubleElements( + FixedArray::cast(from), from_start, + FixedDoubleArray::cast(to), to_start, copy_size); + } else { + UNREACHABLE(); } return to->GetHeap()->undefined_value(); } @@ -762,51 +868,85 @@ class FastObjectElementsAccessor static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, uint32_t capacity, uint32_t length) { - JSObject::SetFastElementsCapacityMode set_capacity_mode = - obj->HasFastSmiOnlyElements() - ? JSObject::kAllowSmiOnlyElements - : JSObject::kDontAllowSmiOnlyElements; + JSObject::SetFastElementsCapacitySmiMode set_capacity_mode = + obj->HasFastSmiElements() + ? JSObject::kAllowSmiElements + : JSObject::kDontAllowSmiElements; return obj->SetFastElementsCapacityAndLength(capacity, length, set_capacity_mode); } +}; - protected: - friend class FastElementsAccessor, - kPointerSize>; - virtual MaybeObject* Delete(JSObject* obj, - uint32_t key, - JSReceiver::DeleteMode mode) { - return DeleteCommon(obj, key); - } +class FastPackedSmiElementsAccessor + : public FastSmiOrObjectElementsAccessor< + FastPackedSmiElementsAccessor, + ElementsKindTraits > { + public: + explicit FastPackedSmiElementsAccessor(const char* name) + : FastSmiOrObjectElementsAccessor< + FastPackedSmiElementsAccessor, + ElementsKindTraits >(name) {} +}; + + +class FastHoleySmiElementsAccessor + : public FastSmiOrObjectElementsAccessor< + FastHoleySmiElementsAccessor, + ElementsKindTraits > { + public: + explicit FastHoleySmiElementsAccessor(const char* name) + : FastSmiOrObjectElementsAccessor< + FastHoleySmiElementsAccessor, + ElementsKindTraits >(name) {} +}; + + +class FastPackedObjectElementsAccessor + : public FastSmiOrObjectElementsAccessor< + FastPackedObjectElementsAccessor, + ElementsKindTraits > { + public: + explicit FastPackedObjectElementsAccessor(const char* name) + : FastSmiOrObjectElementsAccessor< + FastPackedObjectElementsAccessor, + ElementsKindTraits >(name) {} +}; + + +class FastHoleyObjectElementsAccessor + : public FastSmiOrObjectElementsAccessor< + FastHoleyObjectElementsAccessor, + ElementsKindTraits > { + public: + explicit FastHoleyObjectElementsAccessor(const char* name) + : FastSmiOrObjectElementsAccessor< + FastHoleyObjectElementsAccessor, + ElementsKindTraits >(name) {} }; +template class FastDoubleElementsAccessor - : public FastElementsAccessor, + : public FastElementsAccessor { public: explicit FastDoubleElementsAccessor(const char* name) - : FastElementsAccessor, + : FastElementsAccessor(name) {} static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, uint32_t capacity, uint32_t length) { - return obj->SetFastDoubleElementsCapacityAndLength(capacity, length); + return obj->SetFastDoubleElementsCapacityAndLength(capacity, + length); } protected: - friend class ElementsAccessorBase >; - friend class FastElementsAccessor, - kDoubleSize>; - static MaybeObject* CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, FixedArrayBase* to, @@ -814,12 +954,15 @@ class FastDoubleElementsAccessor uint32_t to_start, int copy_size) { switch (to_kind) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: return CopyDoubleToObjectElements( FixedDoubleArray::cast(from), from_start, FixedArray::cast(to), to_kind, to_start, copy_size); case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: CopyDoubleToDoubleElements(FixedDoubleArray::cast(from), from_start, FixedDoubleArray::cast(to), to_start, copy_size); @@ -829,26 +972,35 @@ class FastDoubleElementsAccessor } return to->GetHeap()->undefined_value(); } +}; - virtual MaybeObject* Delete(JSObject* obj, - uint32_t key, - JSReceiver::DeleteMode mode) { - int length = obj->IsJSArray() - ? Smi::cast(JSArray::cast(obj)->length())->value() - : FixedDoubleArray::cast(obj->elements())->length(); - if (key < static_cast(length)) { - FixedDoubleArray::cast(obj->elements())->set_the_hole(key); - } - return obj->GetHeap()->true_value(); - } - static bool HasElementImpl(Object* receiver, - JSObject* holder, - uint32_t key, - FixedDoubleArray* backing_store) { - return key < static_cast(backing_store->length()) && - !backing_store->is_the_hole(key); - } +class FastPackedDoubleElementsAccessor + : public FastDoubleElementsAccessor< + FastPackedDoubleElementsAccessor, + ElementsKindTraits > { + public: + friend class ElementsAccessorBase >; + explicit FastPackedDoubleElementsAccessor(const char* name) + : FastDoubleElementsAccessor< + FastPackedDoubleElementsAccessor, + ElementsKindTraits >(name) {} +}; + + +class FastHoleyDoubleElementsAccessor + : public FastDoubleElementsAccessor< + FastHoleyDoubleElementsAccessor, + ElementsKindTraits > { + public: + friend class ElementsAccessorBase< + FastHoleyDoubleElementsAccessor, + ElementsKindTraits >; + explicit FastHoleyDoubleElementsAccessor(const char* name) + : FastDoubleElementsAccessor< + FastHoleyDoubleElementsAccessor, + ElementsKindTraits >(name) {} }; @@ -1115,13 +1267,16 @@ class DictionaryElementsAccessor uint32_t to_start, int copy_size) { switch (to_kind) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: CopyDictionaryToObjectElements( SeededNumberDictionary::cast(from), from_start, FixedArray::cast(to), to_kind, to_start, copy_size); return from; case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: CopyDictionaryToDoubleElements( SeededNumberDictionary::cast(from), from_start, FixedDoubleArray::cast(to), to_start, copy_size); @@ -1248,7 +1403,10 @@ class NonStrictArgumentsElementsAccessor : public ElementsAccessorBase< if (arguments->IsDictionary()) { return DictionaryElementsAccessor::DeleteCommon(obj, key, mode); } else { - return FastObjectElementsAccessor::DeleteCommon(obj, key); + // It's difficult to access the version of DeleteCommon that is declared + // in the templatized super class, call the concrete implementation in + // the class for the most generalized ElementsKind subclass. + return FastHoleyObjectElementsAccessor::DeleteCommon(obj, key, mode); } } return obj->GetHeap()->true_value(); @@ -1312,7 +1470,7 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { if (array->IsDictionary()) { return elements_accessors_[DICTIONARY_ELEMENTS]; } else { - return elements_accessors_[FAST_ELEMENTS]; + return elements_accessors_[FAST_HOLEY_ELEMENTS]; } case EXTERNAL_BYTE_ARRAY_TYPE: return elements_accessors_[EXTERNAL_BYTE_ELEMENTS]; diff --git a/src/elements.h b/src/elements.h index 55d6fa5..822fca5 100644 --- a/src/elements.h +++ b/src/elements.h @@ -28,6 +28,7 @@ #ifndef V8_ELEMENTS_H_ #define V8_ELEMENTS_H_ +#include "elements-kind.h" #include "objects.h" #include "heap.h" #include "isolate.h" @@ -45,6 +46,10 @@ class ElementsAccessor { virtual ElementsKind kind() const = 0; const char* name() const { return name_; } + // Checks the elements of an object for consistency, asserting when a problem + // is found. + virtual void Validate(JSObject* obj) = 0; + // Returns true if a holder contains an element with the specified key // without iterating up the prototype chain. The caller can optionally pass // in the backing store to use for the check, which must be compatible with diff --git a/src/factory.cc b/src/factory.cc index 6bb7893..1c29ea1 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -775,7 +775,7 @@ Handle Factory::NewFunctionWithPrototype(Handle name, instance_size != JSObject::kHeaderSize) { Handle initial_map = NewMap(type, instance_size, - FAST_SMI_ONLY_ELEMENTS); + GetInitialFastElementsKind()); function->set_initial_map(*initial_map); initial_map->set_constructor(*function); } @@ -1013,10 +1013,11 @@ void Factory::EnsureCanContainHeapObjectElements(Handle array) { void Factory::EnsureCanContainElements(Handle array, Handle elements, + uint32_t length, EnsureElementsMode mode) { CALL_HEAP_FUNCTION_VOID( isolate(), - array->EnsureCanContainElements(*elements, mode)); + array->EnsureCanContainElements(*elements, length, mode)); } diff --git a/src/factory.h b/src/factory.h index 06aad1b..a999b15 100644 --- a/src/factory.h +++ b/src/factory.h @@ -216,9 +216,10 @@ class Factory { Handle NewJSGlobalPropertyCell( Handle value); - Handle NewMap(InstanceType type, - int instance_size, - ElementsKind elements_kind = FAST_ELEMENTS); + Handle NewMap( + InstanceType type, + int instance_size, + ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND); Handle NewFunctionPrototype(Handle function); @@ -269,13 +270,14 @@ class Factory { Handle NewJSModule(); // JS arrays are pretenured when allocated by the parser. - Handle NewJSArray(int capacity, - ElementsKind elements_kind = FAST_ELEMENTS, - PretenureFlag pretenure = NOT_TENURED); + Handle NewJSArray( + int capacity, + ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND, + PretenureFlag pretenure = NOT_TENURED); Handle NewJSArrayWithElements( Handle elements, - ElementsKind elements_kind = FAST_ELEMENTS, + ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND, PretenureFlag pretenure = NOT_TENURED); void SetElementsCapacityAndLength(Handle array, @@ -287,6 +289,7 @@ class Factory { void EnsureCanContainHeapObjectElements(Handle array); void EnsureCanContainElements(Handle array, Handle elements, + uint32_t length, EnsureElementsMode mode); Handle NewJSProxy(Handle handler, Handle prototype); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index ccba511..fc9a1db 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -150,6 +150,7 @@ DEFINE_implication(harmony, harmony_collections) DEFINE_implication(harmony_modules, harmony_scoping) // Flags for experimental implementation features. +DEFINE_bool(packed_arrays, false, "optimizes arrays that have no holes") DEFINE_bool(smi_only_arrays, true, "tracks arrays with only smi values") DEFINE_bool(clever_optimizations, true, diff --git a/src/heap.cc b/src/heap.cc index 47b259e..a224e2b 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2469,7 +2469,7 @@ bool Heap::CreateApiObjects() { // 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); + new_neander_map->set_elements_kind(TERMINAL_FAST_ELEMENTS_KIND); set_neander_map(new_neander_map); { MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map()); @@ -3050,6 +3050,7 @@ MaybeObject* Heap::AllocateJSMessageObject(String* type, } JSMessageObject* message = JSMessageObject::cast(result); message->set_properties(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER); + message->initialize_elements(); message->set_elements(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER); message->set_type(type); message->set_arguments(arguments); @@ -3753,7 +3754,7 @@ MaybeObject* Heap::AllocateArgumentsObject(Object* callee, int length) { // Check the state of the object ASSERT(JSObject::cast(result)->HasFastProperties()); - ASSERT(JSObject::cast(result)->HasFastElements()); + ASSERT(JSObject::cast(result)->HasFastObjectElements()); return result; } @@ -3798,7 +3799,7 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) { map->set_inobject_properties(in_object_properties); map->set_unused_property_fields(in_object_properties); map->set_prototype(prototype); - ASSERT(map->has_fast_elements()); + ASSERT(map->has_fast_object_elements()); // If the function has only simple this property assignments add // field descriptors for these to the initial map as the object @@ -3915,8 +3916,7 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { InitializeJSObjectFromMap(JSObject::cast(obj), FixedArray::cast(properties), map); - ASSERT(JSObject::cast(obj)->HasFastSmiOnlyElements() || - JSObject::cast(obj)->HasFastElements()); + ASSERT(JSObject::cast(obj)->HasFastSmiOrObjectElements()); return obj; } @@ -3961,6 +3961,9 @@ MaybeObject* Heap::AllocateJSArrayAndStorage( ArrayStorageAllocationMode mode, PretenureFlag pretenure) { ASSERT(capacity >= length); + if (length != 0 && mode == INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE) { + elements_kind = GetHoleyElementsKind(elements_kind); + } MaybeObject* maybe_array = AllocateJSArray(elements_kind, pretenure); JSArray* array; if (!maybe_array->To(&array)) return maybe_array; @@ -3981,8 +3984,7 @@ MaybeObject* Heap::AllocateJSArrayAndStorage( maybe_elms = AllocateFixedDoubleArrayWithHoles(capacity); } } else { - ASSERT(elements_kind == FAST_ELEMENTS || - elements_kind == FAST_SMI_ONLY_ELEMENTS); + ASSERT(IsFastSmiOrObjectElementsKind(elements_kind)); if (mode == DONT_INITIALIZE_ARRAY_ELEMENTS) { maybe_elms = AllocateUninitializedFixedArray(capacity); } else { @@ -4008,6 +4010,7 @@ MaybeObject* Heap::AllocateJSArrayWithElements( array->set_elements(elements); array->set_length(Smi::FromInt(elements->length())); + array->ValidateElements(); return array; } @@ -4548,13 +4551,13 @@ MaybeObject* Heap::AllocateJSArray( Context* global_context = isolate()->context()->global_context(); JSFunction* array_function = global_context->array_function(); Map* map = array_function->initial_map(); - if (elements_kind == FAST_DOUBLE_ELEMENTS) { - map = Map::cast(global_context->double_js_array_map()); - } else if (elements_kind == FAST_ELEMENTS || !FLAG_smi_only_arrays) { - map = Map::cast(global_context->object_js_array_map()); - } else { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); - ASSERT(map == global_context->smi_js_array_map()); + Object* maybe_map_array = global_context->js_array_maps(); + if (!maybe_map_array->IsUndefined()) { + Object* maybe_transitioned_map = + FixedArray::cast(maybe_map_array)->get(elements_kind); + if (!maybe_transitioned_map->IsUndefined()) { + map = Map::cast(maybe_transitioned_map); + } } return AllocateJSObjectFromMap(map, pretenure); @@ -4839,9 +4842,7 @@ MaybeObject* Heap::AllocateGlobalContext() { } Context* context = reinterpret_cast(result); context->set_map_no_write_barrier(global_context_map()); - context->set_smi_js_array_map(undefined_value()); - context->set_double_js_array_map(undefined_value()); - context->set_object_js_array_map(undefined_value()); + context->set_js_array_maps(undefined_value()); ASSERT(context->IsGlobalContext()); ASSERT(result->IsContext()); return result; diff --git a/src/heap.h b/src/heap.h index beb1bc5..55e7135 100644 --- a/src/heap.h +++ b/src/heap.h @@ -621,7 +621,7 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateMap( InstanceType instance_type, int instance_size, - ElementsKind elements_kind = FAST_ELEMENTS); + ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND); // 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 c66a7a1..57a1862 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1685,6 +1685,9 @@ void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) { stream->Add("["); key()->PrintNameTo(stream); stream->Add("]"); + if (hole_check_mode_ == PERFORM_HOLE_CHECK) { + stream->Add(" check_hole"); + } } @@ -1736,7 +1739,7 @@ HValue* HLoadKeyedGeneric::Canonicalize() { HInstruction* index = new(block()->zone()) HLoadKeyedFastElement( index_cache, key_load->key(), - HLoadKeyedFastElement::OMIT_HOLE_CHECK); + OMIT_HOLE_CHECK); HLoadFieldByIndex* load = new(block()->zone()) HLoadFieldByIndex( object(), index); map_check->InsertBefore(this); @@ -1784,8 +1787,11 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo( stream->Add("pixel"); break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -1882,9 +1888,12 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( case EXTERNAL_PIXEL_ELEMENTS: stream->Add("pixel"); break; - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -1899,7 +1908,13 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( void HTransitionElementsKind::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); - stream->Add(" %p -> %p", *original_map(), *transitioned_map()); + ElementsKind from_kind = original_map()->elements_kind(); + ElementsKind to_kind = transitioned_map()->elements_kind(); + stream->Add(" %p [%s] -> %p [%s]", + *original_map(), + ElementsAccessor::ForKind(from_kind)->name(), + *transitioned_map(), + ElementsAccessor::ForKind(to_kind)->name()); } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 4f21db7..c68befd 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -2083,28 +2083,21 @@ class HCheckMaps: public HTemplateInstruction<2> { HCheckMaps* check_map = new HCheckMaps(object, map); SmallMapList* map_set = check_map->map_set(); - // If the map to check has the untransitioned elements, it can be hoisted - // above TransitionElements instructions. - if (map->has_fast_smi_only_elements()) { - check_map->ClearGVNFlag(kDependsOnElementsKind); - } - - Map* transitioned_fast_element_map = - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - map_set->Add(Handle(transitioned_fast_element_map)); - } - Map* transitioned_double_map = - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - map_set->Add(Handle(transitioned_double_map)); - } + // Since transitioned elements maps of the initial map don't fail the map + // check, the CheckMaps instruction doesn't need to depend on ElementsKinds. + check_map->ClearGVNFlag(kDependsOnElementsKind); + + ElementsKind kind = map->elements_kind(); + bool packed = IsFastPackedElementsKind(kind); + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + Map* transitioned_map = + map->LookupElementsTransitionMap(kind, NULL); + if (transitioned_map) { + map_set->Add(Handle(transitioned_map)); + } + }; map_set->Sort(); - return check_map; } @@ -3956,11 +3949,12 @@ class ArrayInstructionInterface { virtual ~ArrayInstructionInterface() { }; }; + +enum HoleCheckMode { PERFORM_HOLE_CHECK, OMIT_HOLE_CHECK }; + class HLoadKeyedFastElement : public HTemplateInstruction<2>, public ArrayInstructionInterface { public: - enum HoleCheckMode { PERFORM_HOLE_CHECK, OMIT_HOLE_CHECK }; - HLoadKeyedFastElement(HValue* obj, HValue* key, HoleCheckMode hole_check_mode = PERFORM_HOLE_CHECK) @@ -4015,14 +4009,19 @@ class HLoadKeyedFastElement class HLoadKeyedFastDoubleElement : public HTemplateInstruction<2>, public ArrayInstructionInterface { public: - HLoadKeyedFastDoubleElement(HValue* elements, HValue* key) - : index_offset_(0), is_dehoisted_(false) { - SetOperandAt(0, elements); - SetOperandAt(1, key); - set_representation(Representation::Double()); + HLoadKeyedFastDoubleElement( + HValue* elements, + HValue* key, + HoleCheckMode hole_check_mode = PERFORM_HOLE_CHECK) + : index_offset_(0), + is_dehoisted_(false), + hole_check_mode_(hole_check_mode) { + SetOperandAt(0, elements); + SetOperandAt(1, key); + set_representation(Representation::Double()); SetGVNFlag(kDependsOnDoubleArrayElements); SetFlag(kUseGVN); - } + } HValue* elements() { return OperandAt(0); } HValue* key() { return OperandAt(1); } @@ -4040,16 +4039,26 @@ class HLoadKeyedFastDoubleElement : Representation::Integer32(); } + bool RequiresHoleCheck() { + return hole_check_mode_ == PERFORM_HOLE_CHECK; + } + virtual void PrintDataTo(StringStream* stream); DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastDoubleElement) protected: - virtual bool DataEquals(HValue* other) { return true; } + virtual bool DataEquals(HValue* other) { + if (!other->IsLoadKeyedFastDoubleElement()) return false; + HLoadKeyedFastDoubleElement* other_load = + HLoadKeyedFastDoubleElement::cast(other); + return hole_check_mode_ == other_load->hole_check_mode_; + } private: uint32_t index_offset_; bool is_dehoisted_; + HoleCheckMode hole_check_mode_; }; @@ -4256,7 +4265,7 @@ class HStoreKeyedFastElement HValue* key() { return OperandAt(1); } HValue* value() { return OperandAt(2); } bool value_is_smi() { - return elements_kind_ == FAST_SMI_ONLY_ELEMENTS; + return IsFastSmiElementsKind(elements_kind_); } uint32_t index_offset() { return index_offset_; } void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; } @@ -4427,9 +4436,19 @@ class HTransitionElementsKind: public HTemplateInstruction<1> { transitioned_map_(transitioned_map) { SetOperandAt(0, object); SetFlag(kUseGVN); + // Don't set GVN DependOn flags here. That would defeat GVN's detection of + // congruent HTransitionElementsKind instructions. Instruction hoisting + // handles HTransitionElementsKind instruction specially, explicitly adding + // DependsOn flags during its dependency calculations. SetGVNFlag(kChangesElementsKind); - SetGVNFlag(kChangesElementsPointer); - SetGVNFlag(kChangesNewSpacePromotion); + if (original_map->has_fast_double_elements()) { + SetGVNFlag(kChangesElementsPointer); + SetGVNFlag(kChangesNewSpacePromotion); + } + if (transitioned_map->has_fast_double_elements()) { + SetGVNFlag(kChangesElementsPointer); + SetGVNFlag(kChangesNewSpacePromotion); + } set_representation(Representation::Tagged()); } @@ -4667,7 +4686,7 @@ class HArrayLiteral: public HMaterializedLiteral<1> { HValue* context() { return OperandAt(0); } ElementsKind boilerplate_elements_kind() const { if (!boilerplate_object_->IsJSObject()) { - return FAST_ELEMENTS; + return TERMINAL_FAST_ELEMENTS_KIND; } return Handle::cast(boilerplate_object_)->GetElementsKind(); } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 99beda0..b9ad8af 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1709,23 +1709,23 @@ void HGlobalValueNumberer::ProcessLoopBlock( bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); if (instr->IsTransitionElementsKind()) { // It's possible to hoist transitions out of a loop as long as the - // hoisting wouldn't move the transition past a DependsOn of one of it's - // changes or any instructions that might change an objects map or - // elements contents. - GVNFlagSet changes = instr->ChangesFlags(); + // hoisting wouldn't move the transition past an instruction that has a + // DependsOn flag for anything it changes. GVNFlagSet hoist_depends_blockers = - HValue::ConvertChangesToDependsFlags(changes); - // In addition to not hoisting transitions above other instructions that - // change dependencies that the transition changes, it must not be - // hoisted above map changes and stores to an elements backing store - // that the transition might change. - GVNFlagSet hoist_change_blockers = changes; - hoist_change_blockers.Add(kChangesMaps); + HValue::ConvertChangesToDependsFlags(instr->ChangesFlags()); + + // In addition, the transition must not be hoisted above elements kind + // changes, or if the transition is destructive to the elements buffer, + // changes to array pointer or array contents. + GVNFlagSet hoist_change_blockers; + hoist_change_blockers.Add(kChangesElementsKind); HTransitionElementsKind* trans = HTransitionElementsKind::cast(instr); if (trans->original_map()->has_fast_double_elements()) { + hoist_change_blockers.Add(kChangesElementsPointer); hoist_change_blockers.Add(kChangesDoubleArrayElements); } if (trans->transitioned_map()->has_fast_double_elements()) { + hoist_change_blockers.Add(kChangesElementsPointer); hoist_change_blockers.Add(kChangesArrayElements); } if (FLAG_trace_gvn) { @@ -3966,7 +3966,7 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) { new(zone()) HLoadKeyedFastElement( environment()->ExpressionStackAt(2), // Enum cache. environment()->ExpressionStackAt(0), // Iteration index. - HLoadKeyedFastElement::OMIT_HOLE_CHECK)); + OMIT_HOLE_CHECK)); // Check if the expected map still matches that of the enumerable. // If not just deoptimize. @@ -4257,7 +4257,7 @@ static bool IsFastLiteral(Handle boilerplate, elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) { if (boilerplate->HasFastDoubleElements()) { *total_size += FixedDoubleArray::SizeFor(elements->length()); - } else if (boilerplate->HasFastElements()) { + } else if (boilerplate->HasFastObjectElements()) { Handle fast_elements = Handle::cast(elements); int length = elements->length(); for (int i = 0; i < length; i++) { @@ -4464,11 +4464,13 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { Representation::Integer32())); switch (boilerplate_elements_kind) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: // Smi-only arrays need a smi check. AddInstruction(new(zone()) HCheckSmi(value)); // Fall through. case FAST_ELEMENTS: + case FAST_HOLEY_ELEMENTS: AddInstruction(new(zone()) HStoreKeyedFastElement( elements, key, @@ -4476,6 +4478,7 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { boilerplate_elements_kind)); break; case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements, key, value)); @@ -5233,9 +5236,12 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: break; - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -5260,13 +5266,16 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, ASSERT(val != NULL); switch (elements_kind) { case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: return new(zone()) HStoreKeyedFastDoubleElement( elements, checked_key, val); - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: // Smi-only arrays need a smi check. AddInstruction(new(zone()) HCheckSmi(val)); // Fall through. case FAST_ELEMENTS: + case FAST_HOLEY_ELEMENTS: return new(zone()) HStoreKeyedFastElement( elements, checked_key, val, elements_kind); default: @@ -5275,10 +5284,13 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, } } // It's an element load (!is_store). - if (elements_kind == FAST_DOUBLE_ELEMENTS) { - return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); - } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. - return new(zone()) HLoadKeyedFastElement(elements, checked_key); + HoleCheckMode mode = IsFastPackedElementsKind(elements_kind) ? + OMIT_HOLE_CHECK : + PERFORM_HOLE_CHECK; + if (IsFastDoubleElementsKind(elements_kind)) { + return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key, mode); + } else { // Smi or Object elements. + return new(zone()) HLoadKeyedFastElement(elements, checked_key, mode); } } @@ -5286,15 +5298,30 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, + HValue* dependency, Handle map, bool is_store) { - HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMaps(object, map)); - bool fast_smi_only_elements = map->has_fast_smi_only_elements(); - bool fast_elements = map->has_fast_elements(); + HInstruction* mapcheck = + AddInstruction(new(zone()) HCheckMaps(object, map, dependency)); + // No GVNFlag is necessary for ElementsKind if there is an explicit dependency + // on a HElementsTransition instruction. The flag can also be removed if the + // map to check has FAST_HOLEY_ELEMENTS, since there can be no further + // ElementsKind transitions. Finally, the dependency can be removed for stores + // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the + // generated store code. + if (dependency || + (map->elements_kind() == FAST_HOLEY_ELEMENTS) || + (map->elements_kind() == FAST_ELEMENTS && is_store)) { + mapcheck->ClearGVNFlag(kDependsOnElementsKind); + } + bool fast_smi_only_elements = map->has_fast_smi_elements(); + bool fast_elements = map->has_fast_object_elements(); HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); if (is_store && (fast_elements || fast_smi_only_elements)) { - AddInstruction(new(zone()) HCheckMaps( - elements, isolate()->factory()->fixed_array_map())); + HCheckMaps* check_cow_map = new(zone()) HCheckMaps( + elements, isolate()->factory()->fixed_array_map()); + check_cow_map->ClearGVNFlag(kDependsOnElementsKind); + AddInstruction(check_cow_map); } HInstruction* length = NULL; HInstruction* checked_key = NULL; @@ -5347,8 +5374,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, for (int i = 0; i < maps->length(); ++i) { Handle map = maps->at(i); ElementsKind elements_kind = map->elements_kind(); - if (elements_kind == FAST_DOUBLE_ELEMENTS || - elements_kind == FAST_ELEMENTS) { + if (IsFastElementsKind(elements_kind) && + elements_kind != GetInitialFastElementsKind()) { possible_transitioned_maps.Add(map); } } @@ -5362,12 +5389,17 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, int num_untransitionable_maps = 0; Handle untransitionable_map; + HTransitionElementsKind* transition = NULL; for (int i = 0; i < maps->length(); ++i) { Handle map = maps->at(i); ASSERT(map->IsMap()); if (!transition_target.at(i).is_null()) { - AddInstruction(new(zone()) HTransitionElementsKind( - object, map, transition_target.at(i))); + ASSERT(Map::IsValidElementsTransition( + map->elements_kind(), + transition_target.at(i)->elements_kind())); + transition = new(zone()) HTransitionElementsKind( + object, map, transition_target.at(i)); + AddInstruction(transition); } else { type_todo[map->elements_kind()] = true; if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { @@ -5387,7 +5419,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, : BuildLoadKeyedGeneric(object, key)); } else { instr = AddInstruction(BuildMonomorphicElementAccess( - object, key, val, untransitionable_map, is_store)); + object, key, val, transition, untransitionable_map, is_store)); } *has_side_effects |= instr->HasObservableSideEffects(); instr->set_position(position); @@ -5404,20 +5436,18 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HLoadExternalArrayPointer* external_elements = NULL; HInstruction* checked_key = NULL; - // 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); + // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds + // are handled before external arrays. + STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_HOLEY_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 = FIRST_ELEMENTS_KIND; elements_kind <= LAST_ELEMENTS_KIND; elements_kind = ElementsKind(elements_kind + 1)) { - // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS, - // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code - // that's executed for all external array cases. + // After having handled FAST_* 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 @@ -5439,10 +5469,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_true); HInstruction* access; - if (elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS) { - if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) { + if (IsFastElementsKind(elements_kind)) { + if (is_store && !IsFastDoubleElementsKind(elements_kind)) { AddInstruction(new(zone()) HCheckMaps( elements, isolate()->factory()->fixed_array_map(), elements_kind_branch)); @@ -5529,7 +5557,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, : BuildLoadKeyedGeneric(obj, key); } else { AddInstruction(new(zone()) HCheckNonSmi(obj)); - instr = BuildMonomorphicElementAccess(obj, key, val, map, is_store); + instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store); } } else if (expr->GetReceiverTypes() != NULL && !expr->GetReceiverTypes()->is_empty()) { diff --git a/src/hydrogen.h b/src/hydrogen.h index b56a5af..5c8ddbf 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -1093,6 +1093,7 @@ class HGraphBuilder: public AstVisitor { HInstruction* BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, + HValue* dependency, Handle map, bool is_store); HValue* HandlePolymorphicElementAccess(HValue* object, diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index a36763d..be46ff2 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -900,7 +900,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, const int initial_capacity = JSArray::kPreallocatedArrayElements; STATIC_ASSERT(initial_capacity >= 0); - __ LoadInitialArrayMap(array_function, scratch2, scratch1); + __ LoadInitialArrayMap(array_function, scratch2, scratch1, false); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -1003,7 +1003,8 @@ static void AllocateJSArray(MacroAssembler* masm, ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count ASSERT(!fill_with_hole || !result.is(eax)); // result is never eax - __ LoadInitialArrayMap(array_function, scratch, elements_array); + __ LoadInitialArrayMap(array_function, scratch, + elements_array, fill_with_hole); // Allocate the JSArray object together with space for a FixedArray with the // requested elements. @@ -1274,11 +1275,11 @@ static void ArrayNativeCode(MacroAssembler* masm, __ jmp(&prepare_generic_code_call); __ bind(¬_double); - // Transition FAST_SMI_ONLY_ELEMENTS to FAST_ELEMENTS. + // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS. __ mov(ebx, Operand(esp, 0)); __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset)); __ LoadTransitionedArrayMapConditional( - FAST_SMI_ONLY_ELEMENTS, + FAST_SMI_ELEMENTS, FAST_ELEMENTS, edi, eax, diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index a701d1b..df04b28 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -7063,8 +7063,8 @@ static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { // KeyedStoreStubCompiler::GenerateStoreFastElement. { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET}, { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET}, - // ElementsTransitionGenerator::GenerateSmiOnlyToObject - // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // ElementsTransitionGenerator::GenerateMapChangeElementTransition + // and ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET}, { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET}, @@ -7336,9 +7336,9 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ CheckFastElements(edi, &double_elements); - // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements __ JumpIfSmi(eax, &smi_element); - __ CheckFastSmiOnlyElements(edi, &fast_elements, Label::kNear); + __ CheckFastSmiElements(edi, &fast_elements, Label::kNear); // Store into the array literal requires a elements transition. Call into // the runtime. @@ -7360,7 +7360,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ pop(edx); __ jmp(&slow_elements); - // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. __ bind(&fast_elements); __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size, @@ -7373,15 +7373,15 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { OMIT_SMI_CHECK); __ ret(0); - // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or - // FAST_ELEMENTS, and value is Smi. + // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS, + // and value is Smi. __ bind(&smi_element); __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); __ mov(FieldOperand(ebx, ecx, times_half_pointer_size, FixedArrayBase::kHeaderSize), eax); __ ret(0); - // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS. __ bind(&double_elements); __ push(edx); diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index cff6454..eb68687 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -351,7 +351,7 @@ OS::MemCopyFunction CreateMemCopyFunction() { #define __ ACCESS_MASM(masm) -void ElementsTransitionGenerator::GenerateSmiOnlyToObject( +void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value @@ -372,7 +372,7 @@ void ElementsTransitionGenerator::GenerateSmiOnlyToObject( } -void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( +void ElementsTransitionGenerator::GenerateSmiToDouble( MacroAssembler* masm, Label* fail) { // ----------- S t a t e ------------- // -- eax : value diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 266afce..9727ea0 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1649,7 +1649,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ASSERT_EQ(2, constant_elements->length()); ElementsKind constant_elements_kind = static_cast(Smi::cast(constant_elements->get(0))->value()); - bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; + bool has_constant_fast_elements = + IsFastObjectElementsKind(constant_elements_kind); Handle constant_elements_values( FixedArrayBase::cast(constant_elements->get(1))); @@ -1660,7 +1661,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { Heap* heap = isolate()->heap(); if (has_constant_fast_elements && constant_elements_values->map() == heap->fixed_cow_array_map()) { - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); FastCloneShallowArrayStub stub( @@ -1672,10 +1673,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - ASSERT(constant_elements_kind == FAST_ELEMENTS || - constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || FLAG_smi_only_arrays); - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements ? FastCloneShallowArrayStub::CLONE_ELEMENTS @@ -1703,9 +1703,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - if (constant_elements_kind == FAST_ELEMENTS) { - // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot - // transition and don't need to call the runtime stub. + if (IsFastObjectElementsKind(constant_elements_kind)) { + // Fast-case array literal with ElementsKind of FAST_*_ELEMENTS, they + // cannot transition and don't need to call the runtime stub. int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ mov(ebx, Operand(esp, 0)); // Copy of array literal. __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index dc64a09..a091ff1 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -889,25 +889,25 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, &non_double_value, DONT_DO_SMI_CHECK); - // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> - // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + // Value is a double. Transition FAST_SMI_ELEMENTS -> FAST_DOUBLE_ELEMENTS + // and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, ebx, edi, &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &slow); __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); __ jmp(&fast_double_without_map_check); __ bind(&non_double_value); - // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, ebx, edi, &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm); __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); __ jmp(&finish_object_store); @@ -1622,7 +1622,7 @@ void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { // Must return the modified receiver in eax. if (!FLAG_trace_elements_transitions) { Label fail; - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); __ mov(eax, edx); __ Ret(); __ bind(&fail); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index fa58146..18f2a39 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2377,8 +2377,10 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ movzx_b(temp, FieldOperand(temp, Map::kBitField2Offset)); __ and_(temp, Map::kElementsKindMask); __ shr(temp, Map::kElementsKindShift); - __ cmp(temp, FAST_ELEMENTS); - __ j(equal, &ok, Label::kNear); + __ cmp(temp, GetInitialFastElementsKind()); + __ j(less, &fail, Label::kNear); + __ cmp(temp, TERMINAL_FAST_ELEMENTS_KIND); + __ j(less_equal, &ok, Label::kNear); __ cmp(temp, FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); __ j(less, &fail, Label::kNear); __ cmp(temp, LAST_EXTERNAL_ARRAY_ELEMENTS_KIND); @@ -2439,15 +2441,17 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( LLoadKeyedFastDoubleElement* instr) { XMMRegister result = ToDoubleRegister(instr->result()); - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + - sizeof(kHoleNanLower32); - Operand hole_check_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), - FAST_DOUBLE_ELEMENTS, - offset, - instr->additional_index()); - __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); - DeoptimizeIf(equal, instr->environment()); + if (instr->hydrogen()->RequiresHoleCheck()) { + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + + sizeof(kHoleNanLower32); + Operand hole_check_operand = BuildFastArrayOperand( + instr->elements(), instr->key(), + FAST_DOUBLE_ELEMENTS, + offset, + instr->additional_index()); + __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); + DeoptimizeIf(equal, instr->environment()); + } Operand double_load_operand = BuildFastArrayOperand( instr->elements(), @@ -2528,9 +2532,12 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3456,9 +3463,12 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3550,22 +3560,23 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map); __ j(not_equal, ¬_applicable); __ mov(new_map_reg, to_map); - if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { Register object_reg = ToRegister(instr->object()); __ mov(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg); // Write barrier. ASSERT_NE(instr->temp_reg(), NULL); __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, ToRegister(instr->temp_reg()), kDontSaveFPRegs); - } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { + } else if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(edx)); ASSERT(new_map_reg.is(ebx)); __ mov(fixed_object_reg, object_reg); CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), RelocInfo::CODE_TARGET, instr); - } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + } else if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(edx)); ASSERT(new_map_reg.is(ebx)); @@ -4425,8 +4436,9 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { // Deopt if the array literal boilerplate ElementsKind is of a type different // than the expected one. The check isn't necessary if the boilerplate has - // already been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(eax, instr->hydrogen()->boilerplate_object()); __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); // Load the map's "bit field 2". We only need the first byte, @@ -4588,8 +4600,9 @@ void LCodeGen::DoFastLiteral(LFastLiteral* instr) { // Deopt if the literal boilerplate ElementsKind is of a type different than // the expected one. The check isn't necessary if the boilerplate has already - // been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(ebx, instr->hydrogen()->boilerplate()); __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); // Load the map's "bit field 2". We only need the first byte, diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index d0cb230..fbc75d0 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -2092,8 +2092,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoTransitionElementsKind( HTransitionElementsKind* instr) { - if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + ElementsKind from_kind = instr->original_map()->elements_kind(); + ElementsKind to_kind = instr->transitioned_map()->elements_kind(); + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { LOperand* object = UseRegister(instr->object()); LOperand* new_map_reg = TempRegister(); LOperand* temp_reg = TempRegister(); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index c31b0c2..1b61486 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -382,10 +382,12 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Map::kMaximumBitField2FastElementValue); + Map::kMaximumBitField2FastHoleyElementValue); j(above, fail, distance); } @@ -393,23 +395,26 @@ void MacroAssembler::CheckFastElements(Register map, void MacroAssembler::CheckFastObjectElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Map::kMaximumBitField2FastSmiOnlyElementValue); + Map::kMaximumBitField2FastHoleySmiElementValue); j(below_equal, fail, distance); cmpb(FieldOperand(map, Map::kBitField2Offset), - Map::kMaximumBitField2FastElementValue); + Map::kMaximumBitField2FastHoleyElementValue); j(above, fail, distance); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Label* fail, - Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); cmpb(FieldOperand(map, Map::kBitField2Offset), - Map::kMaximumBitField2FastSmiOnlyElementValue); + Map::kMaximumBitField2FastHoleySmiElementValue); j(above, fail, distance); } @@ -493,24 +498,18 @@ void MacroAssembler::CompareMap(Register obj, CompareMapMode mode) { cmp(FieldOperand(obj, HeapObject::kMapOffset), map); if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - j(equal, early_success, Label::kNear); - cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle(transitioned_fast_element_map)); - } - - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - j(equal, early_success, Label::kNear); - cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle(transitioned_double_map)); + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind, NULL); + if (!current_map) break; + j(equal, early_success, Label::kNear); + cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle(current_map)); + } } } } @@ -2161,27 +2160,38 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( mov(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - cmp(map_in_out, Operand(scratch, Context::SlotOffset(expected_index))); + mov(scratch, Operand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + + size_t offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + cmp(map_in_out, FieldOperand(scratch, offset)); j(not_equal, no_map_match); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - mov(map_in_out, Operand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + mov(map_in_out, FieldOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; mov(map_out, FieldOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 66d1ce7..c71cad8 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -235,7 +235,8 @@ class MacroAssembler: public Assembler { // Load the initial map for new Arrays from a JSFunction. void LoadInitialArrayMap(Register function_in, Register scratch, - Register map_out); + Register map_out, + bool can_have_holes); // Load the global function with the given index. void LoadGlobalFunction(int index, Register function); @@ -357,9 +358,9 @@ class MacroAssembler: public Assembler { // 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); + void CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by key in diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e148e2f..71740ac 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1462,16 +1462,31 @@ Handle CallStubCompiler::CompileArrayPushCall( __ jmp(&fast_object); // In case of fast smi-only, convert to fast object, otherwise bail out. __ bind(¬_fast_object); - __ CheckFastSmiOnlyElements(ebx, &call_builtin); + __ CheckFastSmiElements(ebx, &call_builtin); // edi: elements array // edx: receiver // ebx: map - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + Label try_holey_map; + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, ebx, edi, + &try_holey_map); + + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + // Restore edi. + __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset)); + __ jmp(&fast_object); + + __ bind(&try_holey_map); + __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, + FAST_HOLEY_ELEMENTS, + ebx, + edi, &call_builtin); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); // Restore edi. __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset)); __ bind(&fast_object); @@ -3818,7 +3833,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( // Check that the key is a smi or a heap number convertible to a smi. GenerateSmiKeyCheck(masm, ecx, ebx, xmm0, xmm1, &miss_force_generic); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ JumpIfNotSmi(eax, &transition_elements_kind); } @@ -3843,7 +3858,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ j(not_equal, &miss_force_generic); __ bind(&finish_store); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { // ecx is a smi, use times_half_pointer_size instead of // times_pointer_size __ mov(FieldOperand(edi, @@ -3851,7 +3866,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( times_half_pointer_size, FixedArray::kHeaderSize), eax); } else { - ASSERT(elements_kind == FAST_ELEMENTS); + ASSERT(IsFastObjectElementsKind(elements_kind)); // Do the store and update the write barrier. // ecx is a smi, use times_half_pointer_size instead of // times_pointer_size diff --git a/src/ic.cc b/src/ic.cc index 9772b94..134ef8b 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1644,8 +1644,7 @@ Handle KeyedIC::ComputeMonomorphicStubWithoutMapCheck( return string_stub(); } else { ASSERT(receiver_map->has_dictionary_elements() || - receiver_map->has_fast_elements() || - receiver_map->has_fast_smi_only_elements() || + receiver_map->has_fast_smi_or_object_elements() || receiver_map->has_fast_double_elements() || receiver_map->has_external_array_elements()); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; @@ -1660,8 +1659,7 @@ Handle KeyedIC::ComputeMonomorphicStub(Handle receiver, StubKind stub_kind, StrictModeFlag strict_mode, Handle generic_stub) { - if (receiver->HasFastElements() || - receiver->HasFastSmiOnlyElements() || + if (receiver->HasFastSmiOrObjectElements() || receiver->HasExternalArrayElements() || receiver->HasFastDoubleElements() || receiver->HasDictionaryElements()) { @@ -1681,15 +1679,26 @@ Handle KeyedIC::ComputeTransitionedMap(Handle receiver, case KeyedIC::STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT: case KeyedIC::STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT: return JSObject::GetElementsTransitionMap(receiver, FAST_ELEMENTS); - break; case KeyedIC::STORE_TRANSITION_SMI_TO_DOUBLE: case KeyedIC::STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE: return JSObject::GetElementsTransitionMap(receiver, FAST_DOUBLE_ELEMENTS); - break; - default: + case KeyedIC::STORE_TRANSITION_HOLEY_SMI_TO_OBJECT: + case KeyedIC::STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: + case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT: + case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: + return JSObject::GetElementsTransitionMap(receiver, + FAST_HOLEY_ELEMENTS); + case KeyedIC::STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE: + case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE: + return JSObject::GetElementsTransitionMap(receiver, + FAST_HOLEY_DOUBLE_ELEMENTS); + case KeyedIC::LOAD: + case KeyedIC::STORE_NO_TRANSITION: + case KeyedIC::STORE_AND_GROW_NO_TRANSITION: UNREACHABLE(); - return Handle::null(); + break; } + return Handle::null(); } @@ -1749,30 +1758,54 @@ KeyedIC::StubKind KeyedStoreIC::GetStubKind(Handle receiver, if (allow_growth) { // Handle growing array in stub if necessary. - if (receiver->HasFastSmiOnlyElements()) { + if (receiver->HasFastSmiElements()) { if (value->IsHeapNumber()) { - return STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE; + if (receiver->HasFastHoleyElements()) { + return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE; + } else { + return STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE; + } } if (value->IsHeapObject()) { - return STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT; + if (receiver->HasFastHoleyElements()) { + return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT; + } else { + return STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT; + } } } else if (receiver->HasFastDoubleElements()) { if (!value->IsSmi() && !value->IsHeapNumber()) { - return STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT; + if (receiver->HasFastHoleyElements()) { + return STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT; + } else { + return STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT; + } } } return STORE_AND_GROW_NO_TRANSITION; } else { // Handle only in-bounds elements accesses. - if (receiver->HasFastSmiOnlyElements()) { + if (receiver->HasFastSmiElements()) { if (value->IsHeapNumber()) { - return STORE_TRANSITION_SMI_TO_DOUBLE; + if (receiver->HasFastHoleyElements()) { + return STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE; + } else { + return STORE_TRANSITION_SMI_TO_DOUBLE; + } } else if (value->IsHeapObject()) { - return STORE_TRANSITION_SMI_TO_OBJECT; + if (receiver->HasFastHoleyElements()) { + return STORE_TRANSITION_HOLEY_SMI_TO_OBJECT; + } else { + return STORE_TRANSITION_SMI_TO_OBJECT; + } } } else if (receiver->HasFastDoubleElements()) { if (!value->IsSmi() && !value->IsHeapNumber()) { - return STORE_TRANSITION_DOUBLE_TO_OBJECT; + if (receiver->HasFastHoleyElements()) { + return STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT; + } else { + return STORE_TRANSITION_DOUBLE_TO_OBJECT; + } } } return STORE_NO_TRANSITION; diff --git a/src/ic.h b/src/ic.h index 3b44abf..c1b9549 100644 --- a/src/ic.h +++ b/src/ic.h @@ -378,10 +378,16 @@ class KeyedIC: public IC { STORE_TRANSITION_SMI_TO_OBJECT, STORE_TRANSITION_SMI_TO_DOUBLE, STORE_TRANSITION_DOUBLE_TO_OBJECT, + STORE_TRANSITION_HOLEY_SMI_TO_OBJECT, + STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE, + STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT, STORE_AND_GROW_NO_TRANSITION, STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT, STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE, - STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT + STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT, + STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT, + STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE, + STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT }; static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION - diff --git a/src/jsregexp.cc b/src/jsregexp.cc index cbd0b26..c5e73ed 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -324,7 +324,7 @@ Handle RegExpImpl::AtomExec(Handle re, index))); if (index == -1) return isolate->factory()->null_value(); } - ASSERT(last_match_info->HasFastElements()); + ASSERT(last_match_info->HasFastObjectElements()); { NoHandleAllocation no_handles; diff --git a/src/lithium.cc b/src/lithium.cc index c41cce8..4ee2a7a 100644 --- a/src/lithium.cc +++ b/src/lithium.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -225,9 +225,12 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) { return 2; case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: return 3; - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: return kPointerSizeLog2; diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index eeb84c3..5a2074e 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -118,7 +118,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Label* gc_required) { const int initial_capacity = JSArray::kPreallocatedArrayElements; STATIC_ASSERT(initial_capacity >= 0); - __ LoadInitialArrayMap(array_function, scratch2, scratch1); + __ LoadInitialArrayMap(array_function, scratch2, scratch1, false); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -214,7 +214,8 @@ static void AllocateJSArray(MacroAssembler* masm, bool fill_with_hole, Label* gc_required) { // Load the initial map from the array function. - __ LoadInitialArrayMap(array_function, scratch2, elements_array_storage); + __ LoadInitialArrayMap(array_function, scratch2, + elements_array_storage, fill_with_hole); if (FLAG_debug_code) { // Assert that array size is not zero. __ Assert( @@ -449,10 +450,10 @@ static void ArrayNativeCode(MacroAssembler* masm, __ Branch(call_generic_code); __ bind(¬_double); - // Transition FAST_SMI_ONLY_ELEMENTS to FAST_ELEMENTS. + // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS. // a3: JSArray __ lw(a2, FieldMemOperand(a3, HeapObject::kMapOffset)); - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, a2, t5, diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index f3dd95b..7175fec 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -7362,8 +7362,8 @@ static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { // KeyedStoreStubCompiler::GenerateStoreFastElement. { REG(a3), REG(a2), REG(t0), EMIT_REMEMBERED_SET }, { REG(a2), REG(a3), REG(t0), EMIT_REMEMBERED_SET }, - // ElementsTransitionGenerator::GenerateSmiOnlyToObject - // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // ElementsTransitionGenerator::GenerateMapChangeElementTransition + // and ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(a2), REG(a3), REG(t5), EMIT_REMEMBERED_SET }, { REG(a2), REG(a3), REG(t5), OMIT_REMEMBERED_SET }, @@ -7629,9 +7629,9 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { Label fast_elements; __ CheckFastElements(a2, t1, &double_elements); - // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements __ JumpIfSmi(a0, &smi_element); - __ CheckFastSmiOnlyElements(a2, t1, &fast_elements); + __ CheckFastSmiElements(a2, t1, &fast_elements); // Store into the array literal requires a elements transition. Call into // the runtime. @@ -7643,7 +7643,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ Push(t1, t0); __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); - // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. __ bind(&fast_elements); __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); __ sll(t2, a3, kPointerSizeLog2 - kSmiTagSize); @@ -7656,8 +7656,8 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ Ret(USE_DELAY_SLOT); __ mov(v0, a0); - // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or - // FAST_ELEMENTS, and value is Smi. + // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS, + // and value is Smi. __ bind(&smi_element); __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); __ sll(t2, a3, kPointerSizeLog2 - kSmiTagSize); @@ -7666,7 +7666,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ Ret(USE_DELAY_SLOT); __ mov(v0, a0); - // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS. __ bind(&double_elements); __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); __ StoreNumberToDoubleElements(a0, a3, a1, t1, t2, t3, t5, a2, diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc index 9acccdc..44e0359 100644 --- a/src/mips/codegen-mips.cc +++ b/src/mips/codegen-mips.cc @@ -72,7 +72,7 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { // ------------------------------------------------------------------------- // Code generators -void ElementsTransitionGenerator::GenerateSmiOnlyToObject( +void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( MacroAssembler* masm) { // ----------- S t a t e ------------- // -- a0 : value @@ -95,7 +95,7 @@ void ElementsTransitionGenerator::GenerateSmiOnlyToObject( } -void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( +void ElementsTransitionGenerator::GenerateSmiToDouble( MacroAssembler* masm, Label* fail) { // ----------- S t a t e ------------- // -- a0 : value diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index 7be5056..3ed794a 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -1711,7 +1711,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ASSERT_EQ(2, constant_elements->length()); ElementsKind constant_elements_kind = static_cast(Smi::cast(constant_elements->get(0))->value()); - bool has_fast_elements = constant_elements_kind == FAST_ELEMENTS; + bool has_fast_elements = + IsFastObjectElementsKind(constant_elements_kind); Handle constant_elements_values( FixedArrayBase::cast(constant_elements->get(1))); @@ -1733,8 +1734,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - ASSERT(constant_elements_kind == FAST_ELEMENTS || - constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || FLAG_smi_only_arrays); FastCloneShallowArrayStub::Mode mode = has_fast_elements ? FastCloneShallowArrayStub::CLONE_ELEMENTS @@ -1763,7 +1763,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { VisitForAccumulatorValue(subexpr); - if (constant_elements_kind == FAST_ELEMENTS) { + if (IsFastObjectElementsKind(constant_elements_kind)) { int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ lw(t2, MemOperand(sp)); // Copy of array literal. __ lw(a1, FieldMemOperand(t2, JSObject::kElementsOffset)); diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc index 964a7e2..5d530d0 100644 --- a/src/mips/ic-mips.cc +++ b/src/mips/ic-mips.cc @@ -1347,34 +1347,35 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); __ Branch(&non_double_value, ne, t0, Operand(at)); - // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> - // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + + // Value is a double. Transition FAST_SMI_ELEMENTS -> FAST_DOUBLE_ELEMENTS + // and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, t0, &slow); ASSERT(receiver_map.is(a3)); // Transition code expects map in a3 - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &slow); __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); __ jmp(&fast_double_without_map_check); __ bind(&non_double_value); - // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, receiver_map, t0, &slow); ASSERT(receiver_map.is(a3)); // Transition code expects map in a3 - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm); __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); __ jmp(&finish_object_store); __ bind(&transition_double_elements); - // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a - // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and - // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + // Elements are double, but value is an Object that's not a HeapNumber. Make + // sure that the receiver is a Array with Object elements and transition array + // from double elements to Object elements. __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS, receiver_map, @@ -1471,7 +1472,7 @@ void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { // Must return the modified receiver in v0. if (!FLAG_trace_elements_transitions) { Label fail; - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); __ Ret(USE_DELAY_SLOT); __ mov(v0, a2); __ bind(&fail); diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index a4de721..7094b0a 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -2448,8 +2448,10 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ lbu(scratch, FieldMemOperand(scratch, Map::kBitField2Offset)); __ Ext(scratch, scratch, Map::kElementsKindShift, Map::kElementsKindBitCount); - __ Branch(&done, eq, scratch, - Operand(FAST_ELEMENTS)); + __ Branch(&fail, lt, scratch, + Operand(GetInitialFastElementsKind())); + __ Branch(&done, le, scratch, + Operand(TERMINAL_FAST_ELEMENTS_KIND)); __ Branch(&fail, lt, scratch, Operand(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ Branch(&done, le, scratch, @@ -2542,8 +2544,10 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); } - __ lw(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); - DeoptimizeIf(eq, instr->environment(), scratch, Operand(kHoleNanUpper32)); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ lw(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); + DeoptimizeIf(eq, instr->environment(), scratch, Operand(kHoleNanUpper32)); + } __ ldc1(result, MemOperand(elements)); } @@ -2623,7 +2627,10 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3641,7 +3648,10 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3679,20 +3689,21 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ Branch(¬_applicable, ne, scratch, Operand(from_map)); __ li(new_map_reg, Operand(to_map)); - if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsFastSmiElementsKind(from_kind) && IsFastObjectElementsKind(to_kind)) { __ sw(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset)); // Write barrier. __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, scratch, kRAHasBeenSaved, kDontSaveFPRegs); - } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { + } else if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(a2)); ASSERT(new_map_reg.is(a3)); __ mov(fixed_object_reg, object_reg); CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), RelocInfo::CODE_TARGET, instr); - } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + } else if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(a2)); ASSERT(new_map_reg.is(a3)); @@ -4447,8 +4458,9 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { // Deopt if the array literal boilerplate ElementsKind is of a type different // than the expected one. The check isn't necessary if the boilerplate has - // already been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(a1, instr->hydrogen()->boilerplate_object()); // Load map into a2. __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); @@ -4601,10 +4613,11 @@ void LCodeGen::DoFastLiteral(LFastLiteral* instr) { ElementsKind boilerplate_elements_kind = instr->hydrogen()->boilerplate()->GetElementsKind(); - // Deopt if the literal boilerplate ElementsKind is of a type different than - // the expected one. The check isn't necessary if the boilerplate has already - // been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(a1, instr->hydrogen()->boilerplate()); // Load map into a2. __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index 1eb3ab7..49a462a 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -2023,8 +2023,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoTransitionElementsKind( HTransitionElementsKind* instr) { - if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + ElementsKind from_kind = instr->original_map()->elements_kind(); + ElementsKind to_kind = instr->transitioned_map()->elements_kind(); + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { LOperand* object = UseRegister(instr->object()); LOperand* new_map_reg = TempRegister(); LTransitionElementsKind* result = diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index 2c2445b..6cd5e97 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -3341,33 +3341,39 @@ void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue)); + Branch(fail, hi, scratch, + Operand(Map::kMaximumBitField2FastHoleyElementValue)); } void MacroAssembler::CheckFastObjectElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); Branch(fail, ls, scratch, - Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); Branch(fail, hi, scratch, - Operand(Map::kMaximumBitField2FastElementValue)); + Operand(Map::kMaximumBitField2FastHoleyElementValue)); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); Branch(fail, hi, scratch, - Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); } @@ -3469,22 +3475,17 @@ void MacroAssembler::CompareMapAndBranch(Register obj, lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); Operand right = Operand(map); if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - Branch(early_success, eq, scratch, right); - right = Operand(Handle(transitioned_fast_element_map)); - } - - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - Branch(early_success, eq, scratch, right); - right = Operand(Handle(transitioned_double_map)); + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind, NULL); + if (!current_map) break; + Branch(early_success, eq, scratch, right); + right = Operand(Handle(current_map)); + } } } @@ -4443,27 +4444,37 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( lw(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - lw(at, MemOperand(scratch, Context::SlotOffset(expected_index))); - Branch(no_map_match, ne, map_in_out, Operand(at)); + lw(scratch, + MemOperand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + size_t offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + Branch(no_map_match, ne, map_in_out, Operand(scratch)); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - lw(map_in_out, MemOperand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + lw(map_in_out, FieldMemOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; lw(map_out, FieldMemOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index f57418f..1766866 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -819,7 +819,8 @@ class MacroAssembler: public Assembler { // Load the initial map for new Arrays from a JSFunction. void LoadInitialArrayMap(Register function_in, Register scratch, - Register map_out); + Register map_out, + bool can_have_holes); void LoadGlobalFunction(int index, Register function); @@ -961,9 +962,9 @@ class MacroAssembler: public Assembler { // 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, - Register scratch, - Label* fail); + void CheckFastSmiElements(Register map, + Register scratch, + Label* fail); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by key in diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc index 18a5f5f..f8cf970 100644 --- a/src/mips/stub-cache-mips.cc +++ b/src/mips/stub-cache-mips.cc @@ -1585,16 +1585,29 @@ Handle CallStubCompiler::CompileArrayPushCall( __ jmp(&fast_object); // In case of fast smi-only, convert to fast object, otherwise bail out. __ bind(¬_fast_object); - __ CheckFastSmiOnlyElements(a3, t3, &call_builtin); + __ CheckFastSmiElements(a3, t3, &call_builtin); // edx: receiver // r3: map - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + Label try_holey_map; + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, a3, t3, + &try_holey_map); + __ mov(a2, receiver); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + __ jmp(&fast_object); + + __ bind(&try_holey_map); + __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, + FAST_HOLEY_ELEMENTS, + a3, + t3, &call_builtin); __ mov(a2, receiver); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); __ bind(&fast_object); } else { __ CheckFastObjectElements(a3, a3, &call_builtin); @@ -3372,9 +3385,12 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3508,8 +3524,11 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( } break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3869,8 +3888,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3934,8 +3956,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -4106,8 +4131,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -4286,7 +4314,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( // Check that the key is a smi or a heap number convertible to a smi. GenerateSmiKeyCheck(masm, key_reg, t0, t1, f2, &miss_force_generic); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ JumpIfNotSmi(value_reg, &transition_elements_kind); } @@ -4314,7 +4342,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ bind(&finish_store); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ Addu(scratch, elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); @@ -4323,7 +4351,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ Addu(scratch, scratch, scratch2); __ sw(value_reg, MemOperand(scratch)); } else { - ASSERT(elements_kind == FAST_ELEMENTS); + ASSERT(IsFastObjectElementsKind(elements_kind)); __ Addu(scratch, elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); @@ -4332,7 +4360,6 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ Addu(scratch, scratch, scratch2); __ sw(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/objects-debug.cc b/src/objects-debug.cc index 89b25bd..11a3df9 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -286,12 +286,11 @@ void JSObject::JSObjectVerify() { (map()->inobject_properties() + properties()->length() - map()->NextFreePropertyIndex())); } - ASSERT_EQ((map()->has_fast_elements() || - map()->has_fast_smi_only_elements() || + ASSERT_EQ((map()->has_fast_smi_or_object_elements() || (elements() == GetHeap()->empty_fixed_array())), (elements()->map() == GetHeap()->fixed_array_map() || elements()->map() == GetHeap()->fixed_cow_array_map())); - ASSERT(map()->has_fast_elements() == HasFastElements()); + ASSERT(map()->has_fast_object_elements() == HasFastObjectElements()); } @@ -517,7 +516,7 @@ void JSGlobalProxy::JSGlobalProxyVerify() { VerifyObjectField(JSGlobalProxy::kContextOffset); // Make sure that this object has no properties, elements. CHECK_EQ(0, properties()->length()); - CHECK(HasFastElements()); + CHECK(HasFastObjectElements()); CHECK_EQ(0, FixedArray::cast(elements())->length()); } @@ -812,6 +811,11 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) { } // Indexed properties switch (GetElementsKind()) { + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: case FAST_ELEMENTS: { info->number_of_objects_with_fast_elements_++; int holes = 0; @@ -825,6 +829,14 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) { info->number_of_fast_unused_elements_ += holes; break; } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: case EXTERNAL_PIXEL_ELEMENTS: { info->number_of_objects_with_fast_elements_++; ExternalPixelArray* e = ExternalPixelArray::cast(elements()); @@ -838,8 +850,7 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) { dict->Capacity() - dict->NumberOfElements(); break; } - default: - UNREACHABLE(); + case NON_STRICT_ARGUMENTS_ELEMENTS: break; } } diff --git a/src/objects-inl.h b/src/objects-inl.h index 5444438..4afbe3e 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -128,18 +128,6 @@ PropertyDetails PropertyDetails::AsDeleted() { } -bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, - ElementsKind to_kind) { - if (to_kind == FAST_ELEMENTS) { - return from_kind == FAST_SMI_ONLY_ELEMENTS || - from_kind == FAST_DOUBLE_ELEMENTS; - } else { - return to_kind == FAST_DOUBLE_ELEMENTS && - from_kind == FAST_SMI_ONLY_ELEMENTS; - } -} - - bool Object::IsFixedArrayBase() { return IsFixedArray() || IsFixedDoubleArray(); } @@ -1244,35 +1232,26 @@ FixedArrayBase* JSObject::elements() { return static_cast(array); } -void JSObject::ValidateSmiOnlyElements() { + +void JSObject::ValidateElements() { #if DEBUG - if (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->IsTheHole()); - } - } + if (FLAG_enable_slow_asserts) { + ElementsAccessor* accessor = GetElementsAccessor(); + accessor->Validate(this); } #endif } MaybeObject* JSObject::EnsureCanContainHeapObjectElements() { -#if DEBUG - ValidateSmiOnlyElements(); -#endif - if ((map()->elements_kind() != FAST_ELEMENTS)) { - return TransitionElementsKind(FAST_ELEMENTS); + ValidateElements(); + ElementsKind elements_kind = map()->elements_kind(); + if (!IsFastObjectElementsKind(elements_kind)) { + if (IsFastHoleyElementsKind(elements_kind)) { + return TransitionElementsKind(FAST_HOLEY_ELEMENTS); + } else { + return TransitionElementsKind(FAST_ELEMENTS); + } } return this; } @@ -1284,20 +1263,34 @@ MaybeObject* JSObject::EnsureCanContainElements(Object** objects, ElementsKind current_kind = map()->elements_kind(); ElementsKind target_kind = current_kind; ASSERT(mode != ALLOW_COPIED_DOUBLE_ELEMENTS); - if (current_kind == FAST_ELEMENTS) return this; - + bool is_holey = IsFastHoleyElementsKind(current_kind); + if (current_kind == FAST_HOLEY_ELEMENTS) return this; Heap* heap = GetHeap(); Object* the_hole = heap->the_hole_value(); Object* heap_number_map = heap->heap_number_map(); for (uint32_t i = 0; i < count; ++i) { Object* current = *objects++; - if (!current->IsSmi() && current != the_hole) { + if (current == the_hole) { + is_holey = true; + target_kind = GetHoleyElementsKind(target_kind); + } else if (!current->IsSmi()) { if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && - HeapObject::cast(current)->map() == heap_number_map) { - target_kind = FAST_DOUBLE_ELEMENTS; + HeapObject::cast(current)->map() == heap_number_map && + IsFastSmiElementsKind(target_kind)) { + if (is_holey) { + target_kind = FAST_HOLEY_DOUBLE_ELEMENTS; + } else { + target_kind = FAST_DOUBLE_ELEMENTS; + } } else { - target_kind = FAST_ELEMENTS; - break; + if (!current->IsNumber()) { + if (is_holey) { + target_kind = FAST_HOLEY_ELEMENTS; + break; + } else { + target_kind = FAST_ELEMENTS; + } + } } } } @@ -1310,6 +1303,7 @@ MaybeObject* JSObject::EnsureCanContainElements(Object** objects, MaybeObject* JSObject::EnsureCanContainElements(FixedArrayBase* elements, + uint32_t length, EnsureElementsMode mode) { if (elements->map() != GetHeap()->fixed_double_array_map()) { ASSERT(elements->map() == GetHeap()->fixed_array_map() || @@ -1318,11 +1312,19 @@ MaybeObject* JSObject::EnsureCanContainElements(FixedArrayBase* elements, mode = DONT_ALLOW_DOUBLE_ELEMENTS; } Object** objects = FixedArray::cast(elements)->GetFirstElementAddress(); - return EnsureCanContainElements(objects, elements->length(), mode); + return EnsureCanContainElements(objects, length, mode); } ASSERT(mode == ALLOW_COPIED_DOUBLE_ELEMENTS); - if (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS) { + if (GetElementsKind() == FAST_HOLEY_SMI_ELEMENTS) { + return TransitionElementsKind(FAST_HOLEY_DOUBLE_ELEMENTS); + } else if (GetElementsKind() == FAST_SMI_ELEMENTS) { + FixedDoubleArray* double_array = FixedDoubleArray::cast(elements); + for (uint32_t i = 0; i < length; ++i) { + if (double_array->is_the_hole(i)) { + return TransitionElementsKind(FAST_HOLEY_DOUBLE_ELEMENTS); + } + } return TransitionElementsKind(FAST_DOUBLE_ELEMENTS); } @@ -1334,21 +1336,20 @@ MaybeObject* JSObject::GetElementsTransitionMap(Isolate* isolate, ElementsKind to_kind) { Map* current_map = map(); ElementsKind from_kind = current_map->elements_kind(); - if (from_kind == to_kind) return current_map; Context* global_context = isolate->context()->global_context(); - if (current_map == global_context->smi_js_array_map()) { - if (to_kind == FAST_ELEMENTS) { - return global_context->object_js_array_map(); - } else { - if (to_kind == FAST_DOUBLE_ELEMENTS) { - return global_context->double_js_array_map(); - } else { - ASSERT(to_kind == DICTIONARY_ELEMENTS); + Object* maybe_array_maps = global_context->js_array_maps(); + if (maybe_array_maps->IsFixedArray()) { + FixedArray* array_maps = FixedArray::cast(maybe_array_maps); + if (array_maps->get(from_kind) == current_map) { + Object* maybe_transitioned_map = array_maps->get(to_kind); + if (maybe_transitioned_map->IsMap()) { + return Map::cast(maybe_transitioned_map); } } } + return GetElementsTransitionMapSlow(to_kind); } @@ -1357,9 +1358,6 @@ void JSObject::set_map_and_elements(Map* new_map, FixedArrayBase* value, WriteBarrierMode mode) { ASSERT(value->HasValidElements()); -#ifdef DEBUG - ValidateSmiOnlyElements(); -#endif if (new_map != NULL) { if (mode == UPDATE_WRITE_BARRIER) { set_map(new_map); @@ -1368,8 +1366,7 @@ void JSObject::set_map_and_elements(Map* new_map, set_map_no_write_barrier(new_map); } } - ASSERT((map()->has_fast_elements() || - map()->has_fast_smi_only_elements() || + ASSERT((map()->has_fast_smi_or_object_elements() || (value == GetHeap()->empty_fixed_array())) == (value->map() == GetHeap()->fixed_array_map() || value->map() == GetHeap()->fixed_cow_array_map())); @@ -1392,8 +1389,7 @@ void JSObject::initialize_properties() { void JSObject::initialize_elements() { - ASSERT(map()->has_fast_elements() || - map()->has_fast_smi_only_elements() || + ASSERT(map()->has_fast_smi_or_object_elements() || map()->has_fast_double_elements()); ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array())); WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array()); @@ -1402,9 +1398,10 @@ void JSObject::initialize_elements() { MaybeObject* JSObject::ResetElements() { Object* obj; - ElementsKind elements_kind = FLAG_smi_only_arrays - ? FAST_SMI_ONLY_ELEMENTS - : FAST_ELEMENTS; + ElementsKind elements_kind = GetInitialFastElementsKind(); + if (!FLAG_smi_only_arrays) { + elements_kind = FastSmiToObjectElementsKind(elements_kind); + } MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), elements_kind); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -1676,6 +1673,11 @@ Object* FixedArray::get(int index) { } +bool FixedArray::is_the_hole(int index) { + return get(index) == GetHeap()->the_hole_value(); +} + + void FixedArray::set(int index, Smi* value) { ASSERT(map() != HEAP->fixed_cow_array_map()); ASSERT(index >= 0 && index < this->length()); @@ -2857,15 +2859,15 @@ bool Map::has_non_instance_prototype() { void Map::set_function_with_prototype(bool value) { if (value) { - set_bit_field2(bit_field2() | (1 << kFunctionWithPrototype)); + set_bit_field3(bit_field3() | (1 << kFunctionWithPrototype)); } else { - set_bit_field2(bit_field2() & ~(1 << kFunctionWithPrototype)); + set_bit_field3(bit_field3() & ~(1 << kFunctionWithPrototype)); } } bool Map::function_with_prototype() { - return ((1 << kFunctionWithPrototype) & bit_field2()) != 0; + return ((1 << kFunctionWithPrototype) & bit_field3()) != 0; } @@ -4008,27 +4010,32 @@ MaybeObject* JSFunction::set_initial_map_and_cache_transitions( global_context->get(Context::ARRAY_FUNCTION_INDEX); if (array_function->IsJSFunction() && this == JSFunction::cast(array_function)) { - ASSERT(initial_map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - - MaybeObject* maybe_map = initial_map->CopyDropTransitions(); - Map* new_double_map = NULL; - if (!maybe_map->To(&new_double_map)) return maybe_map; - new_double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); - maybe_map = initial_map->AddElementsTransition(FAST_DOUBLE_ELEMENTS, - new_double_map); - if (maybe_map->IsFailure()) return maybe_map; - - maybe_map = new_double_map->CopyDropTransitions(); - Map* new_object_map = NULL; - if (!maybe_map->To(&new_object_map)) return maybe_map; - new_object_map->set_elements_kind(FAST_ELEMENTS); - maybe_map = new_double_map->AddElementsTransition(FAST_ELEMENTS, - new_object_map); - if (maybe_map->IsFailure()) return maybe_map; - - global_context->set_smi_js_array_map(initial_map); - global_context->set_double_js_array_map(new_double_map); - global_context->set_object_js_array_map(new_object_map); + // Replace all of the cached initial array maps in the global context with + // the appropriate transitioned elements kind maps. + Heap* heap = GetHeap(); + MaybeObject* maybe_maps = + heap->AllocateFixedArrayWithHoles(kElementsKindCount); + FixedArray* maps; + if (!maybe_maps->To(&maps)) return maybe_maps; + + Map* current_map = initial_map; + ElementsKind kind = current_map->elements_kind(); + ASSERT(kind == GetInitialFastElementsKind()); + maps->set(kind, current_map); + for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; + i < kFastElementsKindCount; ++i) { + ElementsKind transitioned_kind = GetFastElementsKindFromSequenceIndex(i); + MaybeObject* maybe_new_map = current_map->CopyDropTransitions(); + Map* new_map = NULL; + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + new_map->set_elements_kind(transitioned_kind); + maybe_new_map = current_map->AddElementsTransition(transitioned_kind, + new_map); + if (maybe_new_map->IsFailure()) return maybe_new_map; + maps->set(transitioned_kind, new_map); + current_map = new_map; + } + global_context->set_js_array_maps(maps); } set_initial_map(initial_map); return this; @@ -4364,18 +4371,18 @@ ElementsKind JSObject::GetElementsKind() { 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 && - (fixed_array->IsFixedDoubleArray() || - fixed_array == GetHeap()->empty_fixed_array())) || - (kind == DICTIONARY_ELEMENTS && + ASSERT((IsFastSmiOrObjectElementsKind(kind) && + (map == GetHeap()->fixed_array_map() || + map == GetHeap()->fixed_cow_array_map())) || + (IsFastDoubleElementsKind(kind) && + (fixed_array->IsFixedDoubleArray() || + fixed_array == GetHeap()->empty_fixed_array())) || + (kind == DICTIONARY_ELEMENTS && fixed_array->IsFixedArray() && - fixed_array->IsDictionary()) || - (kind > DICTIONARY_ELEMENTS)); - ASSERT((kind != NON_STRICT_ARGUMENTS_ELEMENTS) || - (elements()->IsFixedArray() && elements()->length() >= 2)); + fixed_array->IsDictionary()) || + (kind > DICTIONARY_ELEMENTS)); + ASSERT((kind != NON_STRICT_ARGUMENTS_ELEMENTS) || + (elements()->IsFixedArray() && elements()->length() >= 2)); #endif return kind; } @@ -4386,25 +4393,28 @@ ElementsAccessor* JSObject::GetElementsAccessor() { } -bool JSObject::HasFastElements() { - return GetElementsKind() == FAST_ELEMENTS; +bool JSObject::HasFastObjectElements() { + return IsFastObjectElementsKind(GetElementsKind()); } -bool JSObject::HasFastSmiOnlyElements() { - return GetElementsKind() == FAST_SMI_ONLY_ELEMENTS; +bool JSObject::HasFastSmiElements() { + return IsFastSmiElementsKind(GetElementsKind()); } -bool JSObject::HasFastTypeElements() { - ElementsKind elements_kind = GetElementsKind(); - return elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_ELEMENTS; +bool JSObject::HasFastSmiOrObjectElements() { + return IsFastSmiOrObjectElementsKind(GetElementsKind()); } bool JSObject::HasFastDoubleElements() { - return GetElementsKind() == FAST_DOUBLE_ELEMENTS; + return IsFastDoubleElementsKind(GetElementsKind()); +} + + +bool JSObject::HasFastHoleyElements() { + return IsFastHoleyElementsKind(GetElementsKind()); } @@ -4461,7 +4471,7 @@ bool JSObject::HasIndexedInterceptor() { MaybeObject* JSObject::EnsureWritableFastElements() { - ASSERT(HasFastTypeElements()); + ASSERT(HasFastSmiOrObjectElements()); FixedArray* elems = FixedArray::cast(elements()); Isolate* isolate = GetIsolate(); if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems; @@ -4819,7 +4829,7 @@ void Map::ClearCodeCache(Heap* heap) { void JSArray::EnsureSize(int required_size) { - ASSERT(HasFastTypeElements()); + ASSERT(HasFastSmiOrObjectElements()); FixedArray* elts = FixedArray::cast(elements()); const int kArraySizeThatFitsComfortablyInNewSpace = 128; if (elts->length() < required_size) { @@ -4851,13 +4861,13 @@ bool JSArray::AllowsSetElementsLength() { MaybeObject* JSArray::SetContent(FixedArrayBase* storage) { MaybeObject* maybe_result = EnsureCanContainElements( - storage, ALLOW_COPIED_DOUBLE_ELEMENTS); + storage, storage->length(), ALLOW_COPIED_DOUBLE_ELEMENTS); if (maybe_result->IsFailure()) return maybe_result; ASSERT((storage->map() == GetHeap()->fixed_double_array_map() && - GetElementsKind() == FAST_DOUBLE_ELEMENTS) || + IsFastDoubleElementsKind(GetElementsKind())) || ((storage->map() != GetHeap()->fixed_double_array_map()) && - ((GetElementsKind() == FAST_ELEMENTS) || - (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS && + (IsFastObjectElementsKind(GetElementsKind()) || + (IsFastSmiElementsKind(GetElementsKind()) && FixedArray::cast(storage)->ContainsOnlySmisOrHoles())))); set_elements(storage); set_length(Smi::FromInt(storage->length())); diff --git a/src/objects-printer.cc b/src/objects-printer.cc index febdaab..3aea5f0 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -318,7 +318,9 @@ void JSObject::PrintElements(FILE* out) { // 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_HOLEY_SMI_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: case FAST_ELEMENTS: { // Print in array notation for non-sparse arrays. FixedArray* p = FixedArray::cast(elements()); @@ -329,6 +331,7 @@ void JSObject::PrintElements(FILE* out) { } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { // Print in array notation for non-sparse arrays. if (elements()->length() > 0) { diff --git a/src/objects.cc b/src/objects.cc index cb87c71..8cf7a58 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -56,11 +56,6 @@ namespace v8 { namespace internal { -void PrintElementsKind(FILE* out, ElementsKind kind) { - ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); - PrintF(out, "%s", accessor->name()); -} - MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, Object* value) { @@ -543,7 +538,7 @@ bool JSObject::IsDirty() { // If the object is fully fast case and has the same map it was // created with then no changes can have been made to it. return map() != fun->initial_map() - || !HasFastElements() + || !HasFastObjectElements() || !HasFastProperties(); } @@ -1067,7 +1062,9 @@ void String::StringShortPrint(StringStream* accumulator) { void JSObject::JSObjectShortPrint(StringStream* accumulator) { switch (map()->instance_type()) { case JS_ARRAY_TYPE: { - double length = JSArray::cast(this)->length()->Number(); + double length = JSArray::cast(this)->length()->IsUndefined() + ? 0 + : JSArray::cast(this)->length()->Number(); accumulator->Add("", static_cast(length)); break; } @@ -2202,34 +2199,29 @@ static Handle MaybeNull(T* p) { Handle Map::FindTransitionedMap(MapHandleList* candidates) { - ElementsKind elms_kind = elements_kind(); - if (elms_kind == FAST_DOUBLE_ELEMENTS) { - bool dummy = true; - Handle fast_map = - MaybeNull(LookupElementsTransitionMap(FAST_ELEMENTS, &dummy)); - if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { - return fast_map; - } - return Handle::null(); - } - if (elms_kind == FAST_SMI_ONLY_ELEMENTS) { - bool dummy = true; - Handle double_map = - MaybeNull(LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, &dummy)); - // In the current implementation, if the DOUBLE map doesn't exist, the - // FAST map can't exist either. - if (double_map.is_null()) return Handle::null(); - Handle fast_map = - MaybeNull(double_map->LookupElementsTransitionMap(FAST_ELEMENTS, - &dummy)); - if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { - return fast_map; - } - if (ContainsMap(candidates, double_map)) return double_map; - } - return Handle::null(); + ElementsKind kind = elements_kind(); + Handle transitioned_map = Handle::null(); + Handle current_map(this); + bool packed = IsFastPackedElementsKind(kind); + if (IsTransitionableFastElementsKind(kind)) { + while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) { + kind = GetNextMoreGeneralFastElementsKind(kind, false); + bool dummy = true; + Handle maybe_transitioned_map = + MaybeNull(current_map->LookupElementsTransitionMap(kind, &dummy)); + if (maybe_transitioned_map.is_null()) break; + if (ContainsMap(candidates, maybe_transitioned_map) && + (packed || !IsFastPackedElementsKind(kind))) { + transitioned_map = maybe_transitioned_map; + if (!IsFastPackedElementsKind(kind)) packed = false; + } + current_map = maybe_transitioned_map; + } + } + return transitioned_map; } + static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, ElementsKind elements_kind) { if (descriptor_contents->IsMap()) { @@ -2338,24 +2330,36 @@ Object* Map::GetDescriptorContents(String* sentinel_name, } -Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, +Map* Map::LookupElementsTransitionMap(ElementsKind to_kind, bool* safe_to_add_transition) { - // Special case: indirect SMI->FAST transition (cf. comment in - // AddElementsTransition()). - if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - elements_kind == FAST_ELEMENTS) { - Map* double_map = this->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, - safe_to_add_transition); - if (double_map == NULL) return double_map; - return double_map->LookupElementsTransitionMap(FAST_ELEMENTS, + ElementsKind from_kind = elements_kind(); + if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) { + if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { + if (safe_to_add_transition) *safe_to_add_transition = false; + return NULL; + } + ElementsKind transitioned_from_kind = + GetNextMoreGeneralFastElementsKind(from_kind, false); + + + // If the transition is a single step in the transition sequence, fall + // through to looking it up and returning it. If it requires several steps, + // divide and conquer. + if (transitioned_from_kind != to_kind) { + // If the transition is several steps in the lattice, divide and conquer. + Map* from_map = LookupElementsTransitionMap(transitioned_from_kind, + safe_to_add_transition); + if (from_map == NULL) return NULL; + return from_map->LookupElementsTransitionMap(to_kind, safe_to_add_transition); + } } Object* descriptor_contents = GetDescriptorContents( elements_transition_sentinel_name(), safe_to_add_transition); if (descriptor_contents != NULL) { Map* maybe_transition_map = GetElementsTransitionMapFromDescriptor(descriptor_contents, - elements_kind); + to_kind); ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap()); return maybe_transition_map; } @@ -2363,29 +2367,35 @@ Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, } -MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind, +MaybeObject* Map::AddElementsTransition(ElementsKind to_kind, Map* transitioned_map) { - // The map transition graph should be a tree, therefore the transition - // from SMI to FAST elements is not done directly, but by going through - // DOUBLE elements first. - if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - elements_kind == FAST_ELEMENTS) { - bool safe_to_add = true; - Map* double_map = this->LookupElementsTransitionMap( - FAST_DOUBLE_ELEMENTS, &safe_to_add); - // This method is only called when safe_to_add_transition has been found - // to be true earlier. - ASSERT(safe_to_add); - - if (double_map == NULL) { - MaybeObject* maybe_map = this->CopyDropTransitions(); - if (!maybe_map->To(&double_map)) return maybe_map; - double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); - MaybeObject* maybe_double_transition = this->AddElementsTransition( - FAST_DOUBLE_ELEMENTS, double_map); - if (maybe_double_transition->IsFailure()) return maybe_double_transition; - } - return double_map->AddElementsTransition(FAST_ELEMENTS, transitioned_map); + ElementsKind from_kind = elements_kind(); + if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) { + ASSERT(IsMoreGeneralElementsKindTransition(from_kind, to_kind)); + ElementsKind transitioned_from_kind = + GetNextMoreGeneralFastElementsKind(from_kind, false); + // The map transitions graph should be a tree, therefore transitions to + // ElementsKind that are not adjacent in the ElementsKind sequence are not + // done directly, but instead by going through intermediate ElementsKinds + // first. + if (to_kind != transitioned_from_kind) { + bool safe_to_add = true; + Map* intermediate_map = LookupElementsTransitionMap( + transitioned_from_kind, &safe_to_add); + // This method is only called when safe_to_add has been found to be true + // earlier. + ASSERT(safe_to_add); + + if (intermediate_map == NULL) { + MaybeObject* maybe_map = CopyDropTransitions(); + if (!maybe_map->To(&intermediate_map)) return maybe_map; + intermediate_map->set_elements_kind(transitioned_from_kind); + MaybeObject* maybe_transition = AddElementsTransition( + transitioned_from_kind, intermediate_map); + if (maybe_transition->IsFailure()) return maybe_transition; + } + return intermediate_map->AddElementsTransition(to_kind, transitioned_map); + } } bool safe_to_add_transition = true; @@ -2437,10 +2447,11 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { !current_map->IsUndefined() && !current_map->is_shared(); - // Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps caused by objects + // Prevent long chains of DICTIONARY -> FAST_*_ELEMENTS maps caused by objects // with elements that switch back and forth between dictionary and fast - // element mode. - if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) { + // element modes. + if (from_kind == DICTIONARY_ELEMENTS && + IsFastElementsKind(to_kind)) { safe_to_add_transition = false; } @@ -3476,8 +3487,7 @@ MaybeObject* JSObject::NormalizeElements() { } if (array->IsDictionary()) return array; - ASSERT(HasFastElements() || - HasFastSmiOnlyElements() || + ASSERT(HasFastSmiOrObjectElements() || HasFastDoubleElements() || HasFastArgumentsElements()); // Compute the effective length and allocate a new backing store. @@ -3512,8 +3522,7 @@ MaybeObject* JSObject::NormalizeElements() { if (!maybe_value_object->ToObject(&value)) return maybe_value_object; } } else { - ASSERT(old_map->has_fast_elements() || - old_map->has_fast_smi_only_elements()); + ASSERT(old_map->has_fast_smi_or_object_elements()); value = FixedArray::cast(array)->get(i); } PropertyDetails details = PropertyDetails(NONE, NORMAL); @@ -4000,9 +4009,9 @@ MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { bool JSObject::ReferencesObjectFromElements(FixedArray* elements, ElementsKind kind, Object* object) { - ASSERT(kind == FAST_ELEMENTS || + ASSERT(IsFastObjectElementsKind(kind) || kind == DICTIONARY_ELEMENTS); - if (kind == FAST_ELEMENTS) { + if (IsFastObjectElementsKind(kind)) { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : elements->length(); @@ -4054,12 +4063,15 @@ bool JSObject::ReferencesObject(Object* obj) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: // Raw pixels and external arrays do not reference other // objects. break; - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: break; case FAST_ELEMENTS: + case FAST_HOLEY_ELEMENTS: case DICTIONARY_ELEMENTS: { FixedArray* elements = FixedArray::cast(this->elements()); if (ReferencesObjectFromElements(elements, kind, obj)) return true; @@ -4075,7 +4087,8 @@ bool JSObject::ReferencesObject(Object* obj) { } // Check the arguments. FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); - kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS; + kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : + FAST_HOLEY_ELEMENTS; if (ReferencesObjectFromElements(arguments, kind, obj)) return true; break; } @@ -4309,7 +4322,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { } -// Search object and it's prototype chain for callback properties. +// Search object and its prototype chain for callback properties. void JSObject::LookupCallback(String* name, LookupResult* result) { Heap* heap = GetHeap(); for (Object* current = this; @@ -4353,9 +4366,12 @@ MaybeObject* JSObject::DefineElementAccessor(uint32_t index, Object* setter, PropertyAttributes attributes) { switch (GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: break; case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: @@ -4445,7 +4461,7 @@ bool JSObject::CanSetCallback(String* name) { GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); // Check if there is an API defined callback object which prohibits - // callback overwriting in this object or it's prototype chain. + // callback overwriting in this object or its prototype chain. // This mechanism is needed for instance in a browser setting, where // certain accessors such as window.location should not be allowed // to be overwritten because allowing overwriting could potentially @@ -4616,9 +4632,12 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { // Accessors overwrite previous callbacks (cf. with getters/setters). switch (GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: break; case EXTERNAL_PIXEL_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS: @@ -8407,7 +8426,7 @@ void Code::Disassemble(const char* name, FILE* out) { MaybeObject* JSObject::SetFastElementsCapacityAndLength( int capacity, int length, - SetFastElementsCapacityMode set_capacity_mode) { + SetFastElementsCapacitySmiMode smi_mode) { Heap* heap = GetHeap(); // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); @@ -8418,34 +8437,40 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( if (!maybe->To(&new_elements)) return maybe; } - // Find the new map to use for this object if there is a map change. - Map* new_map = NULL; - if (elements()->map() != heap->non_strict_arguments_elements_map()) { - // The resized array has FAST_SMI_ONLY_ELEMENTS if the capacity mode forces - // it, or if it's allowed and the old elements array contained only SMIs. - bool has_fast_smi_only_elements = - (set_capacity_mode == kForceSmiOnlyElements) || - ((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(GetIsolate(), elements_kind); - if (!maybe->To(&new_map)) return maybe; + ElementsKind elements_kind = GetElementsKind(); + ElementsKind new_elements_kind; + // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it, + // or if it's allowed and the old elements array contained only SMIs. + bool has_fast_smi_elements = + (smi_mode == kForceSmiElements) || + ((smi_mode == kAllowSmiElements) && HasFastSmiElements()); + if (has_fast_smi_elements) { + if (IsHoleyElementsKind(elements_kind)) { + new_elements_kind = FAST_HOLEY_SMI_ELEMENTS; + } else { + new_elements_kind = FAST_SMI_ELEMENTS; + } + } else { + if (IsHoleyElementsKind(elements_kind)) { + new_elements_kind = FAST_HOLEY_ELEMENTS; + } else { + new_elements_kind = FAST_ELEMENTS; + } } - FixedArrayBase* old_elements = elements(); - ElementsKind elements_kind = GetElementsKind(); ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); - ElementsKind to_kind = (elements_kind == FAST_SMI_ONLY_ELEMENTS) - ? FAST_SMI_ONLY_ELEMENTS - : FAST_ELEMENTS; { MaybeObject* maybe_obj = - accessor->CopyElements(this, new_elements, to_kind); + accessor->CopyElements(this, new_elements, new_elements_kind); if (maybe_obj->IsFailure()) return maybe_obj; } if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { + Map* new_map = map(); + if (new_elements_kind != elements_kind) { + MaybeObject* maybe = + GetElementsTransitionMap(GetIsolate(), new_elements_kind); + if (!maybe->To(&new_map)) return maybe; + } + ValidateElements(); set_map_and_elements(new_map, new_elements); } else { FixedArray* parameter_map = FixedArray::cast(old_elements); @@ -8457,11 +8482,9 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( GetElementsKind(), new_elements); } - // Update the length if necessary. if (IsJSArray()) { JSArray::cast(this)->set_length(Smi::FromInt(length)); } - return new_elements; } @@ -8479,20 +8502,28 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( if (!maybe_obj->To(&elems)) return maybe_obj; } + ElementsKind elements_kind = GetElementsKind(); + ElementsKind new_elements_kind = elements_kind; + if (IsHoleyElementsKind(elements_kind)) { + new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS; + } else { + new_elements_kind = FAST_DOUBLE_ELEMENTS; + } + Map* new_map; { MaybeObject* maybe_obj = - GetElementsTransitionMap(heap->isolate(), FAST_DOUBLE_ELEMENTS); + GetElementsTransitionMap(heap->isolate(), new_elements_kind); if (!maybe_obj->To(&new_map)) return maybe_obj; } FixedArrayBase* old_elements = elements(); - ElementsKind elements_kind = GetElementsKind(); ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); { MaybeObject* maybe_obj = accessor->CopyElements(this, elems, FAST_DOUBLE_ELEMENTS); if (maybe_obj->IsFailure()) return maybe_obj; } if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { + ValidateElements(); set_map_and_elements(new_map, elems); } else { FixedArray* parameter_map = FixedArray::cast(old_elements); @@ -8501,7 +8532,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( if (FLAG_trace_elements_transitions) { PrintElementsTransition(stdout, elements_kind, old_elements, - FAST_DOUBLE_ELEMENTS, elems); + GetElementsKind(), elems); } if (IsJSArray()) { @@ -8781,8 +8812,10 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } switch (GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast (Smi::cast(JSArray::cast(this)->length())->value()) : @@ -8793,7 +8826,8 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } break; } - case FAST_DOUBLE_ELEMENTS: { + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast (Smi::cast(JSArray::cast(this)->length())->value()) : @@ -9077,7 +9111,7 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value, StrictModeFlag strict_mode, bool check_prototype) { - ASSERT(HasFastTypeElements() || + ASSERT(HasFastSmiOrObjectElements() || HasFastArgumentsElements()); FixedArray* backing_store = FixedArray::cast(elements()); @@ -9103,13 +9137,29 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, // Check if the length property of this object needs to be updated. uint32_t array_length = 0; bool must_update_array_length = false; + bool introduces_holes = true; if (IsJSArray()) { CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); + introduces_holes = index > array_length; if (index >= array_length) { must_update_array_length = true; array_length = index + 1; } + } else { + introduces_holes = index >= capacity; + } + + // If the array is growing, and it's not growth by a single element at the + // end, make sure that the ElementsKind is HOLEY. + ElementsKind elements_kind = GetElementsKind(); + if (introduces_holes && + IsFastElementsKind(elements_kind) && + !IsFastHoleyElementsKind(elements_kind)) { + ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); + MaybeObject* maybe = TransitionElementsKind(transitioned_kind); + if (maybe->IsFailure()) return maybe; } + // Check if the capacity of the backing store needs to be increased, or if // a transition to slow elements is necessary. if (index >= capacity) { @@ -9129,42 +9179,44 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, } } // Convert to fast double elements if appropriate. - if (HasFastSmiOnlyElements() && !value->IsSmi() && value->IsNumber()) { + if (HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) { MaybeObject* maybe = SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); if (maybe->IsFailure()) return maybe; FixedDoubleArray::cast(elements())->set(index, value->Number()); + ValidateElements(); return value; } - // Change elements kind from SMI_ONLY to generic FAST if necessary. - if (HasFastSmiOnlyElements() && !value->IsSmi()) { + // Change elements kind from Smi-only to generic FAST if necessary. + if (HasFastSmiElements() && !value->IsSmi()) { Map* new_map; - { MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), - FAST_ELEMENTS); - if (!maybe_new_map->To(&new_map)) return maybe_new_map; - } + ElementsKind kind = HasFastHoleyElements() + ? FAST_HOLEY_ELEMENTS + : FAST_ELEMENTS; + MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), + kind); + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + set_map(new_map); - if (FLAG_trace_elements_transitions) { - PrintElementsTransition(stdout, FAST_SMI_ONLY_ELEMENTS, elements(), - FAST_ELEMENTS, elements()); - } } // Increase backing store capacity if that's been decided previously. if (new_capacity != capacity) { FixedArray* new_elements; - SetFastElementsCapacityMode set_capacity_mode = - value->IsSmi() && HasFastSmiOnlyElements() - ? kAllowSmiOnlyElements - : kDontAllowSmiOnlyElements; + SetFastElementsCapacitySmiMode smi_mode = + value->IsSmi() && HasFastSmiElements() + ? kAllowSmiElements + : kDontAllowSmiElements; { MaybeObject* maybe = SetFastElementsCapacityAndLength(new_capacity, array_length, - set_capacity_mode); + smi_mode); if (!maybe->To(&new_elements)) return maybe; } new_elements->set(index, value); + ValidateElements(); return value; } + // Finally, set the new element and length. ASSERT(elements()->IsFixedArray()); backing_store->set(index, value); @@ -9288,20 +9340,21 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } else { new_length = dictionary->max_number_key() + 1; } - SetFastElementsCapacityMode set_capacity_mode = FLAG_smi_only_arrays - ? kAllowSmiOnlyElements - : kDontAllowSmiOnlyElements; + SetFastElementsCapacitySmiMode smi_mode = FLAG_smi_only_arrays + ? kAllowSmiElements + : kDontAllowSmiElements; bool has_smi_only_elements = false; bool should_convert_to_fast_double_elements = ShouldConvertToFastDoubleElements(&has_smi_only_elements); if (has_smi_only_elements) { - set_capacity_mode = kForceSmiOnlyElements; + smi_mode = kForceSmiElements; } MaybeObject* result = should_convert_to_fast_double_elements ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) : SetFastElementsCapacityAndLength(new_length, new_length, - set_capacity_mode); + smi_mode); + ValidateElements(); if (result->IsFailure()) return result; #ifdef DEBUG if (FLAG_trace_normalization) { @@ -9340,27 +9393,40 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( // If the value object is not a heap number, switch to fast elements and try // again. bool value_is_smi = value->IsSmi(); + bool introduces_holes = true; + uint32_t length = elms_length; + if (IsJSArray()) { + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); + introduces_holes = index > length; + } else { + introduces_holes = index >= elms_length; + } + 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, - kDontAllowSmiOnlyElements); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - return SetFastElement(index, - value, - strict_mode, - check_prototype); + kDontAllowSmiElements); + if (maybe_obj->IsFailure()) return maybe_obj; + maybe_obj = SetFastElement(index, value, strict_mode, check_prototype); + if (maybe_obj->IsFailure()) return maybe_obj; + ValidateElements(); + return maybe_obj; } double double_value = value_is_smi ? static_cast(Smi::cast(value)->value()) : HeapNumber::cast(value)->value(); + // If the array is growing, and it's not growth by a single element at the + // end, make sure that the ElementsKind is HOLEY. + ElementsKind elements_kind = GetElementsKind(); + if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) { + ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); + MaybeObject* maybe = TransitionElementsKind(transitioned_kind); + if (maybe->IsFailure()) return maybe; + } + // Check whether there is extra space in the fixed array. if (index < elms_length) { FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); @@ -9382,13 +9448,11 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( int new_capacity = NewElementsCapacity(index+1); if (!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; - } + MaybeObject* maybe_obj = + SetFastDoubleElementsCapacityAndLength(new_capacity, index + 1); + if (maybe_obj->IsFailure()) return maybe_obj; FixedDoubleArray::cast(elements())->set(index, double_value); + ValidateElements(); return value; } } @@ -9532,10 +9596,13 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); Isolate* isolate = GetIsolate(); switch (GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: return SetFastElement(index, value, strict_mode, check_prototype); case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: return SetFastDoubleElement(index, value, strict_mode, check_prototype); case EXTERNAL_PIXEL_ELEMENTS: { ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); @@ -9626,11 +9693,19 @@ Handle JSObject::TransitionElementsKind(Handle object, MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { ElementsKind from_kind = map()->elements_kind(); + if (IsFastHoleyElementsKind(from_kind)) { + to_kind = GetHoleyElementsKind(to_kind); + } + Isolate* isolate = GetIsolate(); - if ((from_kind == FAST_SMI_ONLY_ELEMENTS || - elements() == isolate->heap()->empty_fixed_array()) && - to_kind == FAST_ELEMENTS) { - ASSERT(from_kind != FAST_ELEMENTS); + if (elements() == isolate->heap()->empty_fixed_array() || + (IsFastSmiOrObjectElementsKind(from_kind) && + IsFastSmiOrObjectElementsKind(to_kind)) || + (from_kind == FAST_DOUBLE_ELEMENTS && + to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) { + ASSERT(from_kind != TERMINAL_FAST_ELEMENTS_KIND); + // No change is needed to the elements() buffer, the transition + // only requires a map change. MaybeObject* maybe_new_map = GetElementsTransitionMap(isolate, to_kind); Map* new_map; if (!maybe_new_map->To(&new_map)) return maybe_new_map; @@ -9657,18 +9732,21 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { } } - if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { MaybeObject* maybe_result = SetFastDoubleElementsCapacityAndLength(capacity, length); if (maybe_result->IsFailure()) return maybe_result; + ValidateElements(); return this; } - if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { MaybeObject* maybe_result = SetFastElementsCapacityAndLength( - capacity, length, kDontAllowSmiOnlyElements); + capacity, length, kDontAllowSmiElements); if (maybe_result->IsFailure()) return maybe_result; + ValidateElements(); return this; } @@ -9682,10 +9760,14 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { // static bool Map::IsValidElementsTransition(ElementsKind from_kind, ElementsKind to_kind) { - return - (from_kind == FAST_SMI_ONLY_ELEMENTS && - (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) || - (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS); + // Transitions can't go backwards. + if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { + return false; + } + + // Transitions from HOLEY -> PACKED are not allowed. + return !IsFastHoleyElementsKind(from_kind) || + IsFastHoleyElementsKind(to_kind); } @@ -9776,8 +9858,10 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { break; } // Fall through. - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: backing_store = FixedArray::cast(backing_store_base); *capacity = backing_store->length(); for (int i = 0; i < *capacity; ++i) { @@ -9791,7 +9875,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { *used = dictionary->NumberOfElements(); break; } - case FAST_DOUBLE_ELEMENTS: { + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: { FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); *capacity = elms->length(); for (int i = 0; i < *capacity; i++) { @@ -10061,16 +10146,19 @@ 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() ? + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { + uint32_t length = IsJSArray() ? static_cast( Smi::cast(JSArray::cast(this)->length())->value()) : static_cast(FixedArray::cast(elements())->length()); return (index < length) && !FixedArray::cast(elements())->get(index)->IsTheHole(); } - case FAST_DOUBLE_ELEMENTS: { + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast( Smi::cast(JSArray::cast(this)->length())->value()) : @@ -10270,7 +10358,7 @@ int JSObject::NumberOfLocalElements(PropertyAttributes filter) { int JSObject::NumberOfEnumElements() { // Fast case for objects with no elements. - if (!IsJSValue() && HasFastElements()) { + if (!IsJSValue() && HasFastObjectElements()) { uint32_t length = IsJSArray() ? static_cast( Smi::cast(JSArray::cast(this)->length())->value()) : @@ -10286,8 +10374,10 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, PropertyAttributes filter) { int counter = 0; switch (GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : FixedArray::cast(elements())->length(); @@ -10302,7 +10392,8 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, ASSERT(!storage || storage->length() >= counter); break; } - case FAST_DOUBLE_ELEMENTS: { + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : FixedDoubleArray::cast(elements())->length(); @@ -11235,10 +11326,9 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { // Convert to fast elements. Object* obj; - { MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), - FAST_ELEMENTS); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), + FAST_HOLEY_ELEMENTS); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; Map* new_map = Map::cast(obj); PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED; @@ -11249,9 +11339,9 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { } FixedArray* fast_elements = FixedArray::cast(new_array); dict->CopyValuesTo(fast_elements); + ValidateElements(); - set_map(new_map); - set_elements(fast_elements); + set_map_and_elements(new_map, fast_elements); } else if (HasExternalArrayElements()) { // External arrays cannot have holes or undefined elements. return Smi::FromInt(ExternalArray::cast(elements())->length()); @@ -11261,7 +11351,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } } - ASSERT(HasFastTypeElements() || HasFastDoubleElements()); + ASSERT(HasFastSmiOrObjectElements() || 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 421a531..514d4a4 100644 --- a/src/objects.h +++ b/src/objects.h @@ -30,6 +30,7 @@ #include "allocation.h" #include "builtins.h" +#include "elements-kind.h" #include "list.h" #include "property-details.h" #include "smart-array-pointer.h" @@ -131,40 +132,6 @@ namespace v8 { namespace internal { -enum ElementsKind { - // 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. - FAST_DOUBLE_ELEMENTS, - - // The "slow" kind. - DICTIONARY_ELEMENTS, - NON_STRICT_ARGUMENTS_ELEMENTS, - // The "fast" kind for external arrays - EXTERNAL_BYTE_ELEMENTS, - EXTERNAL_UNSIGNED_BYTE_ELEMENTS, - EXTERNAL_SHORT_ELEMENTS, - EXTERNAL_UNSIGNED_SHORT_ELEMENTS, - EXTERNAL_INT_ELEMENTS, - EXTERNAL_UNSIGNED_INT_ELEMENTS, - EXTERNAL_FLOAT_ELEMENTS, - EXTERNAL_DOUBLE_ELEMENTS, - EXTERNAL_PIXEL_ELEMENTS, - - // Derived constants from ElementsKind - FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, - LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, - FIRST_ELEMENTS_KIND = FAST_SMI_ONLY_ELEMENTS, - LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS -}; - enum CompareMapMode { REQUIRE_EXACT_MAP, ALLOW_ELEMENT_TRANSITION_MAPS @@ -175,13 +142,6 @@ enum KeyedAccessGrowMode { ALLOW_JSARRAY_GROWTH }; -const int kElementsKindCount = LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; - -void PrintElementsKind(FILE* out, ElementsKind kind); - -inline bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, - ElementsKind to_kind); - // Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER. enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER }; @@ -1509,13 +1469,19 @@ 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(); + // Returns true if an object has elements of FAST_SMI_ELEMENTS ElementsKind. + inline bool HasFastSmiElements(); + // Returns true if an object has elements of FAST_ELEMENTS ElementsKind. + inline bool HasFastObjectElements(); + // Returns true if an object has elements of FAST_ELEMENTS or + // FAST_SMI_ONLY_ELEMENTS. + inline bool HasFastSmiOrObjectElements(); + // Returns true if an object has elements of FAST_DOUBLE_ELEMENTS + // ElementsKind. inline bool HasFastDoubleElements(); + // Returns true if an object has elements of FAST_HOLEY_*_ELEMENTS + // ElementsKind. + inline bool HasFastHoleyElements(); inline bool HasNonStrictArgumentsElements(); inline bool HasDictionaryElements(); inline bool HasExternalPixelElements(); @@ -1710,7 +1676,7 @@ class JSObject: public JSReceiver { static Handle DeleteElement(Handle obj, uint32_t index); MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode); - inline void ValidateSmiOnlyElements(); + inline void ValidateElements(); // Makes sure that this object can contain HeapObject as elements. MUST_USE_RESULT inline MaybeObject* EnsureCanContainHeapObjectElements(); @@ -1722,6 +1688,7 @@ class JSObject: public JSReceiver { EnsureElementsMode mode); MUST_USE_RESULT inline MaybeObject* EnsureCanContainElements( FixedArrayBase* elements, + uint32_t length, EnsureElementsMode mode); MUST_USE_RESULT MaybeObject* EnsureCanContainElements( Arguments* arguments, @@ -1820,10 +1787,10 @@ class JSObject: public JSReceiver { MUST_USE_RESULT MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index); - enum SetFastElementsCapacityMode { - kAllowSmiOnlyElements, - kForceSmiOnlyElements, - kDontAllowSmiOnlyElements + enum SetFastElementsCapacitySmiMode { + kAllowSmiElements, + kForceSmiElements, + kDontAllowSmiElements }; // Replace the elements' backing store with fast elements of the given @@ -1832,7 +1799,7 @@ class JSObject: public JSReceiver { MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength( int capacity, int length, - SetFastElementsCapacityMode set_capacity_mode); + SetFastElementsCapacitySmiMode smi_mode); MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength( int capacity, int length); @@ -4638,17 +4605,21 @@ class Map: public HeapObject { } // 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; + inline bool has_fast_smi_elements() { + return IsFastSmiElementsKind(elements_kind()); } // Tells whether the instance has fast elements. - inline bool has_fast_elements() { - return elements_kind() == FAST_ELEMENTS; + inline bool has_fast_object_elements() { + return IsFastObjectElementsKind(elements_kind()); + } + + inline bool has_fast_smi_or_object_elements() { + return IsFastSmiOrObjectElementsKind(elements_kind()); } inline bool has_fast_double_elements() { - return elements_kind() == FAST_DOUBLE_ELEMENTS; + return IsFastDoubleElementsKind(elements_kind()); } inline bool has_non_strict_arguments_elements() { @@ -4944,25 +4915,31 @@ class Map: public HeapObject { // Bit positions for bit field 2 static const int kIsExtensible = 0; - static const int kFunctionWithPrototype = 1; - static const int kStringWrapperSafeForDefaultValueOf = 2; - static const int kAttachedToSharedFunctionInfo = 3; + static const int kStringWrapperSafeForDefaultValueOf = 1; + static const int kAttachedToSharedFunctionInfo = 2; // No bits can be used after kElementsKindFirstBit, they are all reserved for // storing ElementKind. - static const int kElementsKindShift = 4; - static const int kElementsKindBitCount = 4; + static const int kElementsKindShift = 3; + static const int kElementsKindBitCount = 5; // Derived values from bit field 2 static const int kElementsKindMask = (-1 << kElementsKindShift) & ((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) << + static const int8_t kMaximumBitField2FastSmiElementValue = + static_cast((FAST_SMI_ELEMENTS + 1) << + Map::kElementsKindShift) - 1; + static const int8_t kMaximumBitField2FastHoleyElementValue = + static_cast((FAST_HOLEY_ELEMENTS + 1) << + Map::kElementsKindShift) - 1; + static const int8_t kMaximumBitField2FastHoleySmiElementValue = + static_cast((FAST_HOLEY_SMI_ELEMENTS + 1) << Map::kElementsKindShift) - 1; // Bit positions for bit field 3 static const int kIsShared = 0; + static const int kFunctionWithPrototype = 1; // Layout of the default cache. It holds alternating name and code objects. static const int kCodeCacheEntrySize = 2; diff --git a/src/parser.cc b/src/parser.cc index 8620519..3a7a973 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -3767,10 +3767,12 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { Handle object_literals = isolate()->factory()->NewFixedArray(values->length(), TENURED); Handle double_literals; - ElementsKind elements_kind = FAST_SMI_ONLY_ELEMENTS; + ElementsKind elements_kind = FAST_SMI_ELEMENTS; bool has_only_undefined_values = true; + bool has_hole_values = false; // Fill in the literals. + Heap* heap = isolate()->heap(); bool is_simple = true; int depth = 1; for (int i = 0, n = values->length(); i < n; i++) { @@ -3779,12 +3781,18 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { depth = m_literal->depth() + 1; } Handle boilerplate_value = GetBoilerplateValue(values->at(i)); - if (boilerplate_value->IsUndefined()) { + if (boilerplate_value->IsTheHole()) { + has_hole_values = true; object_literals->set_the_hole(i); if (elements_kind == FAST_DOUBLE_ELEMENTS) { double_literals->set_the_hole(i); } + } else if (boilerplate_value->IsUndefined()) { is_simple = false; + object_literals->set(i, Smi::FromInt(0)); + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + double_literals->set(i, 0); + } } else { // Examine each literal element, and adjust the ElementsKind if the // literal element is not of a type that can be stored in the current @@ -3794,7 +3802,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { // ultimately end up in FAST_ELEMENTS. has_only_undefined_values = false; object_literals->set(i, *boilerplate_value); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (elements_kind == FAST_SMI_ELEMENTS) { // Smi only elements. Notice if a transition to FAST_DOUBLE_ELEMENTS or // FAST_ELEMENTS is required. if (!boilerplate_value->IsSmi()) { @@ -3842,7 +3850,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { // elements array to a copy-on-write array. if (is_simple && depth == 1 && values->length() > 0 && elements_kind != FAST_DOUBLE_ELEMENTS) { - object_literals->set_map(isolate()->heap()->fixed_cow_array_map()); + object_literals->set_map(heap->fixed_cow_array_map()); } Handle element_values = elements_kind == FAST_DOUBLE_ELEMENTS @@ -3854,6 +3862,10 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { Handle literals = isolate()->factory()->NewFixedArray(2, TENURED); + if (has_hole_values || !FLAG_packed_arrays) { + elements_kind = GetHoleyElementsKind(elements_kind); + } + literals->set(0, Smi::FromInt(elements_kind)); literals->set(1, *element_values); diff --git a/src/profile-generator.cc b/src/profile-generator.cc index b670d4e..0fe7499 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -2207,7 +2207,7 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) { void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) { - if (js_obj->HasFastElements()) { + if (js_obj->HasFastObjectElements()) { FixedArray* elements = FixedArray::cast(js_obj->elements()); int length = js_obj->IsJSArray() ? Smi::cast(JSArray::cast(js_obj)->length())->value() : diff --git a/src/runtime.cc b/src/runtime.cc index a3c159a..af9cef5 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -208,8 +208,10 @@ 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: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); if (elements->map() == heap->fixed_cow_array_map()) { isolate->counters()->cow_arrays_created_runtime()->Increment(); @@ -223,7 +225,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, Object* value = elements->get(i); ASSERT(value->IsSmi() || value->IsTheHole() || - (copy->GetElementsKind() == FAST_ELEMENTS)); + (IsFastObjectElementsKind(copy->GetElementsKind()))); if (value->IsJSObject()) { JSObject* js_object = JSObject::cast(value); { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, @@ -268,6 +270,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: // No contained objects, nothing to do. break; } @@ -452,7 +455,7 @@ MaybeObject* TransitionElements(Handle object, } -static const int kSmiOnlyLiteralMinimumLength = 1024; +static const int kSmiLiteralMinimumLength = 1024; Handle Runtime::CreateArrayLiteralBoilerplate( @@ -470,23 +473,22 @@ Handle Runtime::CreateArrayLiteralBoilerplate( Handle constant_elements_values( FixedArrayBase::cast(elements->get(1))); + ASSERT(IsFastElementsKind(constant_elements_kind)); Context* global_context = isolate->context()->global_context(); - if (constant_elements_kind == FAST_SMI_ONLY_ELEMENTS) { - object->set_map(Map::cast(global_context->smi_js_array_map())); - } else if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { - object->set_map(Map::cast(global_context->double_js_array_map())); - } else { - object->set_map(Map::cast(global_context->object_js_array_map())); - } + Object* maybe_maps_array = global_context->js_array_maps(); + ASSERT(!maybe_maps_array->IsUndefined()); + Object* maybe_map = FixedArray::cast(maybe_maps_array)->get( + constant_elements_kind); + ASSERT(maybe_map->IsMap()); + object->set_map(Map::cast(maybe_map)); Handle copied_elements_values; - if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastDoubleElementsKind(constant_elements_kind)) { ASSERT(FLAG_smi_only_arrays); copied_elements_values = isolate->factory()->CopyFixedDoubleArray( Handle::cast(constant_elements_values)); } else { - ASSERT(constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || - constant_elements_kind == FAST_ELEMENTS); + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind)); const bool is_cow = (constant_elements_values->map() == isolate->heap()->fixed_cow_array_map()); @@ -522,15 +524,22 @@ Handle Runtime::CreateArrayLiteralBoilerplate( object->set_elements(*copied_elements_values); object->set_length(Smi::FromInt(copied_elements_values->length())); - // Ensure that the boilerplate object has FAST_ELEMENTS, unless the flag is + // Ensure that the boilerplate object has FAST_*_ELEMENTS, unless the flag is // on or the object is larger than the threshold. if (!FLAG_smi_only_arrays && - constant_elements_values->length() < kSmiOnlyLiteralMinimumLength) { - if (object->GetElementsKind() != FAST_ELEMENTS) { - CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); + constant_elements_values->length() < kSmiLiteralMinimumLength) { + ElementsKind elements_kind = object->GetElementsKind(); + if (!IsFastObjectElementsKind(elements_kind)) { + if (IsFastHoleyElementsKind(elements_kind)) { + CHECK(!TransitionElements(object, FAST_HOLEY_ELEMENTS, + isolate)->IsFailure()); + } else { + CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); + } } } + object->ValidateElements(); return object; } @@ -1730,7 +1739,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) { // length of a string, i.e. it is always a Smi. We check anyway for security. CONVERT_SMI_ARG_CHECKED(index, 2); CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); - RUNTIME_ASSERT(last_match_info->HasFastElements()); + RUNTIME_ASSERT(last_match_info->HasFastObjectElements()); RUNTIME_ASSERT(index >= 0); RUNTIME_ASSERT(index <= subject->length()); isolate->counters()->regexp_entry_runtime()->Increment(); @@ -3104,7 +3113,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); bool matched = true; do { - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); // Increase the capacity of the builder before entering local handle-scope, // so its internal buffer can safely allocate a new handle if it grows. builder.EnsureCapacity(parts_added_per_loop); @@ -3201,7 +3210,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( if (match.is_null()) return Failure::Exception(); if (match->IsNull()) return *subject_handle; - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); int start, end; { @@ -3275,7 +3284,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( if (match.is_null()) return Failure::Exception(); if (match->IsNull()) break; - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); HandleScope loop_scope(isolate); { AssertNoAllocation match_info_array_is_not_in_a_handle; @@ -3345,7 +3354,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) { CONVERT_ARG_CHECKED(JSRegExp, regexp, 1); CONVERT_ARG_CHECKED(JSArray, last_match_info, 3); - ASSERT(last_match_info->HasFastElements()); + ASSERT(last_match_info->HasFastObjectElements()); if (replacement->length() == 0) { if (subject->HasOnlyAsciiChars()) { @@ -4028,10 +4037,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExecMultiple) { CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2); CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); - ASSERT(last_match_info->HasFastElements()); + ASSERT(last_match_info->HasFastObjectElements()); ASSERT(regexp->GetFlags().is_global()); Handle result_elements; - if (result_array->HasFastElements()) { + if (result_array->HasFastObjectElements()) { result_elements = Handle(FixedArray::cast(result_array->elements())); } @@ -4333,17 +4342,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) { // JSObject without a string key. If the key is a Smi, check for a // definite out-of-bounds access to elements, which is a strong indicator // that subsequent accesses will also call the runtime. Proactively - // transition elements to FAST_ELEMENTS to avoid excessive boxing of + // transition elements to FAST_*_ELEMENTS to avoid excessive boxing of // doubles for those future calls in the case that the elements would // become FAST_DOUBLE_ELEMENTS. Handle js_object(args.at(0)); ElementsKind elements_kind = js_object->GetElementsKind(); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastElementsKind(elements_kind) && + !IsFastObjectElementsKind(elements_kind)) { FixedArrayBase* elements = js_object->elements(); if (args.at(1)->value() >= elements->length()) { + if (IsFastHoleyElementsKind(elements_kind)) { + elements_kind = FAST_HOLEY_ELEMENTS; + } else { + elements_kind = FAST_ELEMENTS; + } MaybeObject* maybe_object = TransitionElements(js_object, - FAST_ELEMENTS, + elements_kind, isolate); if (maybe_object->IsFailure()) return maybe_object; } @@ -4513,8 +4527,10 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate, return *value; } + js_object->ValidateElements(); Handle result = JSObject::SetElement( js_object, index, value, attr, strict_mode, set_mode); + js_object->ValidateElements(); if (result.is_null()) return Failure::Exception(); return *value; } @@ -4672,7 +4688,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 1); Handle object = args.at(0); - return TransitionElements(object, FAST_DOUBLE_ELEMENTS, isolate); + if (object->IsJSObject()) { + Handle js_object(Handle::cast(object)); + ElementsKind new_kind = js_object->HasFastHoleyElements() + ? FAST_HOLEY_DOUBLE_ELEMENTS + : FAST_DOUBLE_ELEMENTS; + return TransitionElements(object, new_kind, isolate); + } else { + return *object; + } } @@ -4680,7 +4704,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 1); Handle object = args.at(0); - return TransitionElements(object, FAST_ELEMENTS, isolate); + if (object->IsJSObject()) { + Handle js_object(Handle::cast(object)); + ElementsKind new_kind = js_object->HasFastHoleyElements() + ? FAST_HOLEY_ELEMENTS + : FAST_ELEMENTS; + return TransitionElements(object, new_kind, isolate); + } else { + return *object; + } } @@ -4711,32 +4743,38 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) { HandleScope scope; Object* raw_boilerplate_object = literals->get(literal_index); - Handle boilerplate(JSArray::cast(raw_boilerplate_object)); -#if DEBUG + Handle boilerplate_object(JSArray::cast(raw_boilerplate_object)); ElementsKind elements_kind = object->GetElementsKind(); -#endif - ASSERT(elements_kind <= FAST_DOUBLE_ELEMENTS); + ASSERT(IsFastElementsKind(elements_kind)); // Smis should never trigger transitions. ASSERT(!value->IsSmi()); if (value->IsNumber()) { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); - JSObject::TransitionElementsKind(object, FAST_DOUBLE_ELEMENTS); - if (IsMoreGeneralElementsKindTransition(boilerplate->GetElementsKind(), - FAST_DOUBLE_ELEMENTS)) { - JSObject::TransitionElementsKind(boilerplate, FAST_DOUBLE_ELEMENTS); - } - ASSERT(object->GetElementsKind() == FAST_DOUBLE_ELEMENTS); + ASSERT(IsFastSmiElementsKind(elements_kind)); + ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) + ? FAST_HOLEY_DOUBLE_ELEMENTS + : FAST_DOUBLE_ELEMENTS; + if (IsMoreGeneralElementsKindTransition( + boilerplate_object->GetElementsKind(), + transitioned_kind)) { + JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); + } + JSObject::TransitionElementsKind(object, transitioned_kind); + ASSERT(IsFastDoubleElementsKind(object->GetElementsKind())); FixedDoubleArray* double_array = FixedDoubleArray::cast(object->elements()); HeapNumber* number = HeapNumber::cast(*value); double_array->set(store_index, number->Number()); } else { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS); - JSObject::TransitionElementsKind(object, FAST_ELEMENTS); - if (IsMoreGeneralElementsKindTransition(boilerplate->GetElementsKind(), - FAST_ELEMENTS)) { - JSObject::TransitionElementsKind(boilerplate, FAST_ELEMENTS); + ASSERT(IsFastSmiElementsKind(elements_kind) || + IsFastDoubleElementsKind(elements_kind)); + ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) + ? FAST_HOLEY_ELEMENTS + : FAST_ELEMENTS; + JSObject::TransitionElementsKind(object, transitioned_kind); + if (IsMoreGeneralElementsKindTransition( + boilerplate_object->GetElementsKind(), + transitioned_kind)) { + JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); } FixedArray* object_array = FixedArray::cast(object->elements()); object_array->set(store_index, *value); @@ -5949,7 +5987,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringArray) { ASSERT(args.length() == 1); CONVERT_ARG_CHECKED(JSArray, array, 0); - if (!array->HasFastElements()) return isolate->heap()->undefined_value(); + if (!array->HasFastObjectElements()) { + return isolate->heap()->undefined_value(); + } FixedArray* elements = FixedArray::cast(array->elements()); int n = elements->length(); bool ascii = true; @@ -6392,7 +6432,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { if (maybe_result->IsFailure()) return maybe_result; result->set_length(Smi::FromInt(part_count)); - ASSERT(result->HasFastElements()); + ASSERT(result->HasFastObjectElements()); if (part_count == 1 && indices.at(0) == subject_length) { FixedArray::cast(result->elements())->set(0, *subject); @@ -6411,7 +6451,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { } if (limit == 0xffffffffu) { - if (result->HasFastElements()) { + if (result->HasFastObjectElements()) { StringSplitCache::Enter(isolate->heap(), isolate->heap()->string_split_cache(), *subject, @@ -6768,7 +6808,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { if (maybe_result->IsFailure()) return maybe_result; int special_length = special->length(); - if (!array->HasFastElements()) { + if (!array->HasFastObjectElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); } FixedArray* fixed_array = FixedArray::cast(array->elements()); @@ -6878,7 +6918,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { int array_length = args.smi_at(1); CONVERT_ARG_CHECKED(String, separator, 2); - if (!array->HasFastElements()) { + if (!array->HasFastObjectElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); } FixedArray* fixed_array = FixedArray::cast(array->elements()); @@ -6995,8 +7035,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { NoHandleAllocation ha; ASSERT(args.length() == 3); CONVERT_ARG_CHECKED(JSArray, elements_array, 0); - RUNTIME_ASSERT(elements_array->HasFastElements() || - elements_array->HasFastSmiOnlyElements()); + RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements()); CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); CONVERT_ARG_CHECKED(String, separator, 2); // elements_array is fast-mode JSarray of alternating positions @@ -9157,7 +9196,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) { MaybeObject* maybe_result_array = output->EnsureCanContainHeapObjectElements(); if (maybe_result_array->IsFailure()) return maybe_result_array; - RUNTIME_ASSERT(output->HasFastElements()); + RUNTIME_ASSERT(output->HasFastObjectElements()); AssertNoAllocation no_allocation; @@ -9389,7 +9428,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) { ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(JSArray, array, 0); CONVERT_ARG_CHECKED(JSObject, element, 1); - RUNTIME_ASSERT(array->HasFastElements() || array->HasFastSmiOnlyElements()); + RUNTIME_ASSERT(array->HasFastSmiOrObjectElements()); int length = Smi::cast(array->length())->value(); FixedArray* elements = FixedArray::cast(array->elements()); for (int i = 0; i < length; i++) { @@ -9474,7 +9513,7 @@ class ArrayConcatVisitor { Handle map; if (fast_elements_) { map = isolate_->factory()->GetElementsTransitionMap(array, - FAST_ELEMENTS); + FAST_HOLEY_ELEMENTS); } else { map = isolate_->factory()->GetElementsTransitionMap(array, DICTIONARY_ELEMENTS); @@ -9533,8 +9572,10 @@ static uint32_t EstimateElementCount(Handle array) { uint32_t length = static_cast(array->length()->Number()); int element_count = 0; switch (array->GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { // Fast elements can't have lengths that are not representable by // a 32-bit signed integer. ASSERT(static_cast(FixedArray::kMaxLength) >= 0); @@ -9546,6 +9587,7 @@ static uint32_t EstimateElementCount(Handle array) { break; } case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); break; @@ -9636,8 +9678,10 @@ static void CollectElementIndices(Handle object, List* indices) { ElementsKind kind = object->GetElementsKind(); switch (kind) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { Handle elements(FixedArray::cast(object->elements())); uint32_t length = static_cast(elements->length()); if (range < length) length = range; @@ -9648,6 +9692,7 @@ static void CollectElementIndices(Handle object, } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); @@ -9762,8 +9807,10 @@ 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: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. Handle elements(FixedArray::cast(receiver->elements())); @@ -9784,6 +9831,7 @@ static bool IterateElements(Isolate* isolate, } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); @@ -9881,7 +9929,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) { CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); int argument_count = static_cast(arguments->length()->Number()); - RUNTIME_ASSERT(arguments->HasFastElements()); + RUNTIME_ASSERT(arguments->HasFastObjectElements()); Handle elements(FixedArray::cast(arguments->elements())); // Pass 1: estimate the length and number of elements of the result. @@ -9901,10 +9949,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) { Handle array(Handle::cast(obj)); // TODO(1810): Find out if it's worthwhile to properly support // arbitrary ElementsKinds. For now, pessimistically transition to - // FAST_ELEMENTS. + // FAST_*_ELEMENTS. if (array->HasFastDoubleElements()) { + ElementsKind to_kind = FAST_ELEMENTS; + if (array->HasFastHoleyElements()) { + to_kind = FAST_HOLEY_ELEMENTS; + } array = Handle::cast( - JSObject::TransitionElementsKind(array, FAST_ELEMENTS)); + JSObject::TransitionElementsKind(array, to_kind)); } length_estimate = static_cast(array->length()->Number()); @@ -10001,29 +10053,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) { ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(JSArray, from, 0); CONVERT_ARG_CHECKED(JSArray, to, 1); + from->ValidateElements(); + to->ValidateElements(); FixedArrayBase* new_elements = from->elements(); + ElementsKind from_kind = from->GetElementsKind(); MaybeObject* maybe_new_map; - ElementsKind elements_kind; - if (new_elements->map() == isolate->heap()->fixed_array_map() || - new_elements->map() == isolate->heap()->fixed_cow_array_map()) { - elements_kind = FAST_ELEMENTS; - } else if (new_elements->map() == - isolate->heap()->fixed_double_array_map()) { - elements_kind = FAST_DOUBLE_ELEMENTS; - } else { - elements_kind = DICTIONARY_ELEMENTS; - } - maybe_new_map = to->GetElementsTransitionMap(isolate, elements_kind); + maybe_new_map = to->GetElementsTransitionMap(isolate, from_kind); Object* new_map; if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; - to->set_map(Map::cast(new_map)); - to->set_elements(new_elements); + to->set_map_and_elements(Map::cast(new_map), new_elements); to->set_length(from->length()); Object* obj; { MaybeObject* maybe_obj = from->ResetElements(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } from->set_length(Smi::FromInt(0)); + to->ValidateElements(); return to; } @@ -10073,8 +10118,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) { } return *isolate->factory()->NewJSArrayWithElements(keys); } else { - ASSERT(array->HasFastElements() || - array->HasFastSmiOnlyElements() || + ASSERT(array->HasFastSmiOrObjectElements() || array->HasFastDoubleElements()); Handle single_interval = isolate->factory()->NewFixedArray(2); // -1 means start of array. @@ -13389,9 +13433,11 @@ 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(FastSmiElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalPixelElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements) diff --git a/src/runtime.h b/src/runtime.h index 83991bb..fc0a472 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -364,9 +364,11 @@ namespace internal { F(IS_VAR, 1, 1) \ \ /* expose boolean functions from objects-inl.h */ \ - F(HasFastSmiOnlyElements, 1, 1) \ - F(HasFastElements, 1, 1) \ + F(HasFastSmiElements, 1, 1) \ + F(HasFastSmiOrObjectElements, 1, 1) \ + F(HasFastObjectElements, 1, 1) \ F(HasFastDoubleElements, 1, 1) \ + F(HasFastHoleyElements, 1, 1) \ F(HasDictionaryElements, 1, 1) \ F(HasExternalPixelElements, 1, 1) \ F(HasExternalArrayElements, 1, 1) \ diff --git a/src/string-stream.cc b/src/string-stream.cc index 35f7be5..bf711ba 100644 --- a/src/string-stream.cc +++ b/src/string-stream.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -427,7 +427,7 @@ void StringStream::PrintMentionedObjectCache() { PrintUsingMap(JSObject::cast(printee)); if (printee->IsJSArray()) { JSArray* array = JSArray::cast(printee); - if (array->HasFastElements()) { + if (array->HasFastObjectElements()) { unsigned int limit = FixedArray::cast(array->elements())->length(); unsigned int length = static_cast(JSArray::cast(array)->length()->Number()); diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 4e037ff..0af0a43 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -977,7 +977,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, const int initial_capacity = JSArray::kPreallocatedArrayElements; STATIC_ASSERT(initial_capacity >= 0); - __ LoadInitialArrayMap(array_function, scratch2, scratch1); + __ LoadInitialArrayMap(array_function, scratch2, scratch1, false); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -1076,7 +1076,8 @@ static void AllocateJSArray(MacroAssembler* masm, Register scratch, bool fill_with_hole, Label* gc_required) { - __ LoadInitialArrayMap(array_function, scratch, elements_array); + __ LoadInitialArrayMap(array_function, scratch, + elements_array, fill_with_hole); if (FLAG_debug_code) { // Assert that array size is not zero. __ testq(array_size, array_size); @@ -1303,10 +1304,10 @@ static void ArrayNativeCode(MacroAssembler* masm, __ jmp(call_generic_code); __ bind(¬_double); - // Transition FAST_SMI_ONLY_ELEMENTS to FAST_ELEMENTS. + // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS. // rbx: JSArray __ movq(r11, FieldOperand(rbx, HeapObject::kMapOffset)); - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, r11, kScratchRegister, diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 13838ac..61d6c87 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -6002,12 +6002,12 @@ struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { // KeyedStoreStubCompiler::GenerateStoreFastElement. { REG(rdi), REG(rbx), REG(rcx), EMIT_REMEMBERED_SET}, { REG(rdx), REG(rdi), REG(rbx), EMIT_REMEMBERED_SET}, - // ElementsTransitionGenerator::GenerateSmiOnlyToObject - // and ElementsTransitionGenerator::GenerateSmiOnlyToObject + // ElementsTransitionGenerator::GenerateMapChangeElementTransition + // and ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(rdx), REG(rbx), REG(rdi), EMIT_REMEMBERED_SET}, { REG(rdx), REG(rbx), REG(rdi), OMIT_REMEMBERED_SET}, - // ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(rdx), REG(r11), REG(r15), EMIT_REMEMBERED_SET}, // ElementsTransitionGenerator::GenerateDoubleToObject @@ -6281,9 +6281,9 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ CheckFastElements(rdi, &double_elements); - // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS __ JumpIfSmi(rax, &smi_element); - __ CheckFastSmiOnlyElements(rdi, &fast_elements); + __ CheckFastSmiElements(rdi, &fast_elements); // Store into the array literal requires a elements transition. Call into // the runtime. @@ -6301,7 +6301,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { // place. __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); - // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. __ bind(&fast_elements); __ SmiToInteger32(kScratchRegister, rcx); __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); @@ -6315,8 +6315,8 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { OMIT_SMI_CHECK); __ ret(0); - // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or - // FAST_ELEMENTS, and value is Smi. + // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or + // FAST_*_ELEMENTS, and value is Smi. __ bind(&smi_element); __ SmiToInteger32(kScratchRegister, rcx); __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index a8d39b2..2924810 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -220,7 +220,7 @@ ModuloFunction CreateModuloFunction() { #define __ ACCESS_MASM(masm) -void ElementsTransitionGenerator::GenerateSmiOnlyToObject( +void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value @@ -241,7 +241,7 @@ void ElementsTransitionGenerator::GenerateSmiOnlyToObject( } -void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( +void ElementsTransitionGenerator::GenerateSmiToDouble( MacroAssembler* masm, Label* fail) { // ----------- S t a t e ------------- // -- rax : value diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 81dad6b..0db7424 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1659,7 +1659,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ASSERT_EQ(2, constant_elements->length()); ElementsKind constant_elements_kind = static_cast(Smi::cast(constant_elements->get(0))->value()); - bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; + bool has_constant_fast_elements = + IsFastObjectElementsKind(constant_elements_kind); Handle constant_elements_values( FixedArrayBase::cast(constant_elements->get(1))); @@ -1670,7 +1671,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { Heap* heap = isolate()->heap(); if (has_constant_fast_elements && constant_elements_values->map() == heap->fixed_cow_array_map()) { - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); FastCloneShallowArrayStub stub( @@ -1682,10 +1683,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - ASSERT(constant_elements_kind == FAST_ELEMENTS || - constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || FLAG_smi_only_arrays); - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements ? FastCloneShallowArrayStub::CLONE_ELEMENTS @@ -1713,9 +1713,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - if (constant_elements_kind == FAST_ELEMENTS) { - // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot - // transition and don't need to call the runtime stub. + if (IsFastObjectElementsKind(constant_elements_kind)) { + // Fast-case array literal with ElementsKind of FAST_*_ELEMENTS, they + // cannot transition and don't need to call the runtime stub. int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 6ba5fb6..82fdb3c 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -769,25 +769,25 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex); __ j(not_equal, &non_double_value); - // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // Value is a double. Transition FAST_SMI_ELEMENTS -> // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, rbx, rdi, &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &slow); __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); __ jmp(&fast_double_without_map_check); __ bind(&non_double_value); - // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, rbx, rdi, &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm); __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); __ jmp(&finish_object_store); @@ -1642,7 +1642,7 @@ void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { // Must return the modified receiver in eax. if (!FLAG_trace_elements_transitions) { Label fail; - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); __ movq(rax, rdx); __ Ret(); __ bind(&fail); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index d1cf338..f1c631b 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2324,8 +2324,10 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ movzxbq(temp, FieldOperand(temp, Map::kBitField2Offset)); __ and_(temp, Immediate(Map::kElementsKindMask)); __ shr(temp, Immediate(Map::kElementsKindShift)); - __ cmpl(temp, Immediate(FAST_ELEMENTS)); - __ j(equal, &ok, Label::kNear); + __ cmpl(temp, Immediate(GetInitialFastElementsKind())); + __ j(less, &fail, Label::kNear); + __ cmpl(temp, Immediate(TERMINAL_FAST_ELEMENTS_KIND)); + __ j(less_equal, &ok, Label::kNear); __ cmpl(temp, Immediate(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ j(less, &fail, Label::kNear); __ cmpl(temp, Immediate(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); @@ -2403,16 +2405,18 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( __ movsxlq(key_reg, key_reg); } - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + - sizeof(kHoleNanLower32); - Operand hole_check_operand = BuildFastArrayOperand( - instr->elements(), - instr->key(), - FAST_DOUBLE_ELEMENTS, - offset, - instr->additional_index()); - __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); - DeoptimizeIf(equal, instr->environment()); + if (instr->hydrogen()->RequiresHoleCheck()) { + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + + sizeof(kHoleNanLower32); + Operand hole_check_operand = BuildFastArrayOperand( + instr->elements(), + instr->key(), + FAST_DOUBLE_ELEMENTS, + offset, + instr->additional_index()); + __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); + DeoptimizeIf(equal, instr->environment()); + } Operand double_load_operand = BuildFastArrayOperand( instr->elements(), @@ -2501,8 +2505,11 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3403,8 +3410,11 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3539,21 +3549,22 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map); __ j(not_equal, ¬_applicable); __ movq(new_map_reg, to_map, RelocInfo::EMBEDDED_OBJECT); - if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { __ movq(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg); // Write barrier. ASSERT_NE(instr->temp_reg(), NULL); __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, ToRegister(instr->temp_reg()), kDontSaveFPRegs); - } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { + } else if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(rdx)); ASSERT(new_map_reg.is(rbx)); __ movq(fixed_object_reg, object_reg); CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), RelocInfo::CODE_TARGET, instr); - } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + } else if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { Register fixed_object_reg = ToRegister(instr->temp_reg()); ASSERT(fixed_object_reg.is(rdx)); ASSERT(new_map_reg.is(rbx)); @@ -4227,8 +4238,9 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { // Deopt if the array literal boilerplate ElementsKind is of a type different // than the expected one. The check isn't necessary if the boilerplate has - // already been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(rax, instr->hydrogen()->boilerplate_object()); __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); // Load the map's "bit field 2". @@ -4374,10 +4386,11 @@ void LCodeGen::DoFastLiteral(LFastLiteral* instr) { ElementsKind boilerplate_elements_kind = instr->hydrogen()->boilerplate()->GetElementsKind(); - // Deopt if the literal boilerplate ElementsKind is of a type different than - // the expected one. The check isn't necessary if the boilerplate has already - // been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(rbx, instr->hydrogen()->boilerplate()); __ movq(rcx, FieldOperand(rbx, HeapObject::kMapOffset)); // Load the map's "bit field 2". diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 3ba0cae..6094dbb 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -2012,8 +2012,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoTransitionElementsKind( HTransitionElementsKind* instr) { - if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + ElementsKind from_kind = instr->original_map()->elements_kind(); + ElementsKind to_kind = instr->transitioned_map()->elements_kind(); + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { LOperand* object = UseRegister(instr->object()); LOperand* new_map_reg = TempRegister(); LOperand* temp_reg = TempRegister(); diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 3d380a2..6308fa3 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2658,10 +2658,12 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastElementValue)); + Immediate(Map::kMaximumBitField2FastHoleyElementValue)); j(above, fail, distance); } @@ -2669,23 +2671,26 @@ void MacroAssembler::CheckFastElements(Register map, void MacroAssembler::CheckFastObjectElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Immediate(Map::kMaximumBitField2FastHoleySmiElementValue)); j(below_equal, fail, distance); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastElementValue)); + Immediate(Map::kMaximumBitField2FastHoleyElementValue)); j(above, fail, distance); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Label* fail, - Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Immediate(Map::kMaximumBitField2FastHoleySmiElementValue)); j(above, fail, distance); } @@ -2749,24 +2754,18 @@ void MacroAssembler::CompareMap(Register obj, CompareMapMode mode) { Cmp(FieldOperand(obj, HeapObject::kMapOffset), map); if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - j(equal, early_success, Label::kNear); - Cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle(transitioned_fast_element_map)); - } - - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - j(equal, early_success, Label::kNear); - Cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle(transitioned_double_map)); + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind, NULL); + if (!current_map) break; + j(equal, early_success, Label::kNear); + Cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle(current_map)); + } } } } @@ -4057,27 +4056,38 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - cmpq(map_in_out, Operand(scratch, Context::SlotOffset(expected_index))); + movq(scratch, Operand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + + size_t offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + cmpq(map_in_out, FieldOperand(scratch, offset)); j(not_equal, no_map_match); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - movq(map_in_out, Operand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + movq(map_in_out, FieldOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; movq(map_out, FieldOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 66587d5..1c1cd95 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -877,9 +877,9 @@ class MacroAssembler: public Assembler { // 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); + void CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by index in @@ -1141,7 +1141,8 @@ class MacroAssembler: public Assembler { // Load the initial map for new Arrays from a JSFunction. void LoadInitialArrayMap(Register function_in, Register scratch, - Register map_out); + Register map_out, + bool can_have_holes); // Load the global function with the given index. void LoadGlobalFunction(int index, Register function); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 5721e9b..1b8ed38 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -1434,17 +1434,32 @@ Handle CallStubCompiler::CompileArrayPushCall( __ jmp(&fast_object); // In case of fast smi-only, convert to fast object, otherwise bail out. __ bind(¬_fast_object); - __ CheckFastSmiOnlyElements(rbx, &call_builtin); + __ CheckFastSmiElements(rbx, &call_builtin); // rdx: receiver // rbx: map - __ movq(r9, rdi); // Backup rdi as it is going to be trashed. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + + Label try_holey_map; + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, rbx, rdi, + &try_holey_map); + + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + // Restore edi. + __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset)); + __ jmp(&fast_object); + + __ bind(&try_holey_map); + __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, + FAST_HOLEY_ELEMENTS, + rbx, + rdi, &call_builtin); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); - __ movq(rdi, r9); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset)); __ bind(&fast_object); } else { __ CheckFastObjectElements(rbx, &call_builtin); @@ -3369,8 +3384,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3435,8 +3453,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3587,7 +3608,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( // Check that the key is a smi or a heap number convertible to a smi. GenerateSmiKeyCheck(masm, rcx, rbx, xmm0, xmm1, &miss_force_generic); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ JumpIfNotSmi(rax, &transition_elements_kind); } @@ -3611,13 +3632,13 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ j(not_equal, &miss_force_generic); __ bind(&finish_store); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ SmiToInteger32(rcx, rcx); __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), rax); } else { // Do the store and update the write barrier. - ASSERT(elements_kind == FAST_ELEMENTS); + ASSERT(IsFastObjectElementsKind(elements_kind)); __ SmiToInteger32(rcx, rcx); __ lea(rcx, FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize)); diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index d4a40bf..33aaed3 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -673,7 +673,7 @@ TEST(JSArray) { array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); CHECK_EQ(Smi::FromInt(0), array->length()); // Must be in fast mode. - CHECK(array->HasFastTypeElements()); + CHECK(array->HasFastSmiOrObjectElements()); // array[length] = name. array->SetElement(0, *name, NONE, kNonStrictMode)->ToObjectChecked(); @@ -811,7 +811,9 @@ TEST(Iteration) { // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE objs[next_objs_index++] = FACTORY->NewJSArray(10); - objs[next_objs_index++] = FACTORY->NewJSArray(10, FAST_ELEMENTS, TENURED); + objs[next_objs_index++] = FACTORY->NewJSArray(10, + FAST_HOLEY_ELEMENTS, + TENURED); // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE objs[next_objs_index++] = @@ -1595,7 +1597,7 @@ TEST(PrototypeTransitionClearing) { Handle prototype; PagedSpace* space = HEAP->old_pointer_space(); do { - prototype = FACTORY->NewJSArray(32 * KB, FAST_ELEMENTS, TENURED); + prototype = FACTORY->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); } while (space->FirstPage() == space->LastPage() || !space->LastPage()->Contains(prototype->address())); diff --git a/test/mjsunit/array-construct-transition.js b/test/mjsunit/array-construct-transition.js index 577e321..f8d7c83 100644 --- a/test/mjsunit/array-construct-transition.js +++ b/test/mjsunit/array-construct-transition.js @@ -27,13 +27,13 @@ // Flags: --allow-natives-syntax --smi-only-arrays -support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); +support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6)); if (support_smi_only_arrays) { var a = new Array(0, 1, 2); - assertTrue(%HasFastSmiOnlyElements(a)); + assertTrue(%HasFastSmiElements(a)); var b = new Array(0.5, 1.2, 2.3); assertTrue(%HasFastDoubleElements(b)); var c = new Array(0.5, 1.2, new Object()); - assertTrue(%HasFastElements(c)); + assertTrue(%HasFastObjectElements(c)); } diff --git a/test/mjsunit/array-literal-transitions.js b/test/mjsunit/array-literal-transitions.js index f657525..a96719d 100644 --- a/test/mjsunit/array-literal-transitions.js +++ b/test/mjsunit/array-literal-transitions.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -33,7 +33,7 @@ // in this test case. Depending on whether smi-only arrays are actually // enabled, this test takes the appropriate code path to check smi-only arrays. -support_smi_only_arrays = %HasFastSmiOnlyElements([1,2,3,4,5,6,7,8,9,10]); +support_smi_only_arrays = %HasFastSmiElements([1,2,3,4,5,6,7,8,9,10]); if (support_smi_only_arrays) { print("Tests include smi-only arrays."); @@ -46,14 +46,14 @@ function get(foo) { return foo; } // Used to generate dynamic values. function array_literal_test() { var a0 = [1, 2, 3]; - assertTrue(%HasFastSmiOnlyElements(a0)); + assertTrue(%HasFastSmiElements(a0)); var a1 = [get(1), get(2), get(3)]; - assertTrue(%HasFastSmiOnlyElements(a1)); + assertTrue(%HasFastSmiElements(a1)); var b0 = [1, 2, get("three")]; - assertTrue(%HasFastElements(b0)); + assertTrue(%HasFastObjectElements(b0)); var b1 = [get(1), get(2), get("three")]; - assertTrue(%HasFastElements(b1)); + assertTrue(%HasFastObjectElements(b1)); var c0 = [1, 2, get(3.5)]; assertTrue(%HasFastDoubleElements(c0)); @@ -75,7 +75,7 @@ function array_literal_test() { var object = new Object(); var d0 = [1, 2, object]; - assertTrue(%HasFastElements(d0)); + assertTrue(%HasFastObjectElements(d0)); assertEquals(object, d0[2]); assertEquals(2, d0[1]); assertEquals(1, d0[0]); @@ -87,7 +87,7 @@ function array_literal_test() { assertEquals(1, e0[0]); var f0 = [1, 2, [1, 2]]; - assertTrue(%HasFastElements(f0)); + assertTrue(%HasFastObjectElements(f0)); assertEquals([1,2], f0[2]); assertEquals(2, f0[1]); assertEquals(1, f0[0]); @@ -115,9 +115,9 @@ if (support_smi_only_arrays) { large = [ 0, 1, 2, 3, 4, 5, d(), d(), d(), d(), d(), d(), o(), o(), o(), o() ]; assertFalse(%HasDictionaryElements(large)); - assertFalse(%HasFastSmiOnlyElements(large)); + assertFalse(%HasFastSmiElements(large)); assertFalse(%HasFastDoubleElements(large)); - assertTrue(%HasFastElements(large)); + assertTrue(%HasFastObjectElements(large)); assertEquals(large, [0, 1, 2, 3, 4, 5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, new Object(), new Object(), new Object(), new Object()]); diff --git a/test/mjsunit/elements-kind.js b/test/mjsunit/elements-kind.js index 4aa79de..26b3c78 100644 --- a/test/mjsunit/elements-kind.js +++ b/test/mjsunit/elements-kind.js @@ -34,7 +34,7 @@ // in this test case. Depending on whether smi-only arrays are actually // enabled, this test takes the appropriate code path to check smi-only arrays. -support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); +support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8)); if (support_smi_only_arrays) { print("Tests include smi-only arrays."); @@ -59,8 +59,8 @@ var elements_kind = { } function getKind(obj) { - if (%HasFastSmiOnlyElements(obj)) return elements_kind.fast_smi_only; - if (%HasFastElements(obj)) return elements_kind.fast; + if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only; + if (%HasFastObjectElements(obj)) return elements_kind.fast; if (%HasFastDoubleElements(obj)) return elements_kind.fast_double; if (%HasDictionaryElements(obj)) return elements_kind.dictionary; // Every external kind is also an external array. @@ -116,7 +116,7 @@ if (support_smi_only_arrays) { assertKind(elements_kind.fast_smi_only, too); } -// Make sure the element kind transitions from smionly when a non-smi is stored. +// Make sure the element kind transitions from smi when a non-smi is stored. var you = new Array(); assertKind(elements_kind.fast_smi_only, you); for (var i = 0; i < 1337; i++) { diff --git a/test/mjsunit/elements-transition-hoisting.js b/test/mjsunit/elements-transition-hoisting.js index 5e78f10..50ca2a1 100644 --- a/test/mjsunit/elements-transition-hoisting.js +++ b/test/mjsunit/elements-transition-hoisting.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -31,7 +31,7 @@ // not hoisted) correctly, don't change the semantics programs and don't trigger // deopt through hoisting in important situations. -support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6)); +support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6)); if (support_smi_only_arrays) { print("Tests include smi-only arrays."); diff --git a/test/mjsunit/elements-transition.js b/test/mjsunit/elements-transition.js index 60e051b..0dffd37 100644 --- a/test/mjsunit/elements-transition.js +++ b/test/mjsunit/elements-transition.js @@ -27,7 +27,7 @@ // Flags: --allow-natives-syntax --smi-only-arrays -support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); +support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8)); if (support_smi_only_arrays) { print("Tests include smi-only arrays."); @@ -44,8 +44,8 @@ if (support_smi_only_arrays) { var array_1 = new Array(length); var array_2 = new Array(length); - assertTrue(%HasFastSmiOnlyElements(array_1)); - assertTrue(%HasFastSmiOnlyElements(array_2)); + assertTrue(%HasFastSmiElements(array_1)); + assertTrue(%HasFastSmiElements(array_2)); for (var i = 0; i < length; i++) { if (i == length - 5 && test_double) { // Trigger conversion to fast double elements at length-5. @@ -57,8 +57,8 @@ if (support_smi_only_arrays) { // Trigger conversion to fast object elements at length-3. set(array_1, i, 'object'); set(array_2, i, 'object'); - assertTrue(%HasFastElements(array_1)); - assertTrue(%HasFastElements(array_2)); + assertTrue(%HasFastObjectElements(array_1)); + assertTrue(%HasFastObjectElements(array_2)); } else if (i != length - 7) { // Set the element to an integer but leave a hole at length-7. set(array_1, i, 2*i+1); diff --git a/test/mjsunit/packed-elements.js b/test/mjsunit/packed-elements.js new file mode 100644 index 0000000..7f333e5 --- /dev/null +++ b/test/mjsunit/packed-elements.js @@ -0,0 +1,112 @@ +// Copyright 2012 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. + +// Flags: --allow-natives-syntax --smi-only-arrays --packed-arrays + +var has_packed_elements = !%HasFastHoleyElements(Array()); + +function test1() { + var a = Array(8); + assertTrue(%HasFastSmiOrObjectElements(a)); + assertTrue(%HasFastHoleyElements(a)); +} + +function test2() { + var a = Array(); + assertTrue(%HasFastSmiOrObjectElements(a)); + assertFalse(%HasFastHoleyElements(a)); +} + +function test3() { + var a = Array(1,2,3,4,5,6,7); + assertTrue(%HasFastSmiOrObjectElements(a)); + assertFalse(%HasFastHoleyElements(a)); +} + +function test4() { + var a = [1, 2, 3, 4]; + assertTrue(%HasFastSmiElements(a)); + assertFalse(%HasFastHoleyElements(a)); + var b = [1, 2,, 4]; + assertTrue(%HasFastSmiElements(b)); + assertTrue(%HasFastHoleyElements(b)); +} + +function test5() { + var a = [1, 2, 3, 4.5]; + assertTrue(%HasFastDoubleElements(a)); + assertFalse(%HasFastHoleyElements(a)); + var b = [1,, 3.5, 4]; + assertTrue(%HasFastDoubleElements(b)); + assertTrue(%HasFastHoleyElements(b)); + var c = [1, 3.5,, 4]; + assertTrue(%HasFastDoubleElements(c)); + assertTrue(%HasFastHoleyElements(c)); +} + +function test6() { + var x = new Object(); + var a = [1, 2, 3.5, x]; + assertTrue(%HasFastObjectElements(a)); + assertFalse(%HasFastHoleyElements(a)); + assertEquals(1, a[0]); + assertEquals(2, a[1]); + assertEquals(3.5, a[2]); + assertEquals(x, a[3]); + var b = [1,, 3.5, x]; + assertTrue(%HasFastObjectElements(b)); + assertTrue(%HasFastHoleyElements(b)); + assertEquals(1, b[0]); + assertEquals(undefined, b[1]); + assertEquals(3.5, b[2]); + assertEquals(x, b[3]); + var c = [1, 3.5, x,,]; + assertTrue(%HasFastObjectElements(c)); + assertTrue(%HasFastHoleyElements(c)); + assertEquals(1, c[0]); + assertEquals(3.5, c[1]); + assertEquals(x, c[2]); + assertEquals(undefined, c[3]); +} + +function test_with_optimization(f) { + // Run tests in a loop to make sure that inlined Array() constructor runs out + // of new space memory and must fall back on runtime impl. + for (i = 0; i < 250000; ++i) f(); + %OptimizeFunctionOnNextCall(f); + for (i = 0; i < 250000; ++i) f(); // Make sure GC happens +} + +if (has_packed_elements) { + test_with_optimization(test1); + test_with_optimization(test2); + test_with_optimization(test3); + test_with_optimization(test4); + test_with_optimization(test5); + test_with_optimization(test6); +} + diff --git a/test/mjsunit/regress/regress-117409.js b/test/mjsunit/regress/regress-117409.js index 9222191..98aab5a 100644 --- a/test/mjsunit/regress/regress-117409.js +++ b/test/mjsunit/regress/regress-117409.js @@ -36,7 +36,7 @@ var literal = [1.2]; KeyedStoreIC(literal); KeyedStoreIC(literal); -// Trruncate array to 0 elements, at which point backing store will be replaced +// Truncate array to 0 elements, at which point backing store will be replaced // with empty fixed array. literal.length = 0; diff --git a/test/mjsunit/regress/regress-1849.js b/test/mjsunit/regress/regress-1849.js index 176f918..5b8fc50 100644 --- a/test/mjsunit/regress/regress-1849.js +++ b/test/mjsunit/regress/regress-1849.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// See: http://code.google.com/p/v8/issues/detail?id=1878 +// See: http://code.google.com/p/v8/issues/detail?id=1849 // Flags: --allow-natives-syntax @@ -36,4 +36,4 @@ for (var i = 0; i < count; i++) { arr[i] = 0; } assertFalse(%HasFastDoubleElements(arr)); -assertTrue(%HasFastSmiOnlyElements(arr)); +assertTrue(%HasFastSmiElements(arr)); diff --git a/test/mjsunit/regress/regress-1878.js b/test/mjsunit/regress/regress-1878.js index a1648b1..fbc47bd 100644 --- a/test/mjsunit/regress/regress-1878.js +++ b/test/mjsunit/regress/regress-1878.js @@ -34,11 +34,11 @@ var a = Array(); for (var i = 0; i < 1000; i++) { var ai = natives.InternalArray(10000); assertFalse(%HaveSameMap(ai, a)); - assertTrue(%HasFastElements(ai)); + assertTrue(%HasFastObjectElements(ai)); } for (var i = 0; i < 1000; i++) { var ai = new natives.InternalArray(10000); assertFalse(%HaveSameMap(ai, a)); - assertTrue(%HasFastElements(ai)); + assertTrue(%HasFastObjectElements(ai)); } diff --git a/test/mjsunit/regress/regress-crbug-122271.js b/test/mjsunit/regress/regress-crbug-122271.js index 3a99a7f..8ae91e8 100644 --- a/test/mjsunit/regress/regress-crbug-122271.js +++ b/test/mjsunit/regress/regress-crbug-122271.js @@ -39,11 +39,11 @@ function foo(array) { array.foo = "bar"; } -assertTrue(%HasFastSmiOnlyElements(a)); -assertTrue(%HasFastElements(b)); +assertTrue(%HasFastSmiElements(a)); +assertTrue(%HasFastObjectElements(b)); foo(a); foo(b); -assertTrue(%HasFastSmiOnlyElements(a)); -assertTrue(%HasFastElements(b)); +assertTrue(%HasFastSmiElements(a)); +assertTrue(%HasFastObjectElements(b)); diff --git a/test/mjsunit/regress/regress-smi-only-concat.js b/test/mjsunit/regress/regress-smi-only-concat.js index a9a6d89..55ca299 100644 --- a/test/mjsunit/regress/regress-smi-only-concat.js +++ b/test/mjsunit/regress/regress-smi-only-concat.js @@ -33,5 +33,5 @@ var fast_array = ['a', 'b']; var array = fast_array.concat(fast_array); -assertTrue(%HasFastElements(fast_array)); -assertTrue(%HasFastElements(array)); \ No newline at end of file +assertTrue(%HasFastObjectElements(fast_array)); +assertTrue(%HasFastObjectElements(array)); diff --git a/test/mjsunit/unbox-double-arrays.js b/test/mjsunit/unbox-double-arrays.js index fd7db28..d5f7816 100644 --- a/test/mjsunit/unbox-double-arrays.js +++ b/test/mjsunit/unbox-double-arrays.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -376,7 +376,7 @@ delete large_array2[5]; // Convert back to fast elements and make sure the contents of the array are // unchanged. large_array2[25] = new Object(); -assertTrue(%HasFastElements(large_array2)); +assertTrue(%HasFastObjectElements(large_array2)); for (var i= 0; i < approx_dict_to_elements_threshold; i += 500 ) { if (i != 25 && i != 5) { assertEquals(expected_array_value(i), large_array2[i]); diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index aa91139..ea82d31 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -295,6 +295,8 @@ '../../src/dtoa.h', '../../src/elements.cc', '../../src/elements.h', + '../../src/elements-kind.cc', + '../../src/elements-kind.h', '../../src/execution.cc', '../../src/execution.h', '../../src/factory.cc', -- 2.7.4