From 78037d0a4e8e4a9635eedec08aa1e38fb764007c Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Tue, 28 Aug 2012 14:20:50 +0000 Subject: [PATCH] Use a special EnumLength field to indicate number of valid enum cache values. This is preparatory work for sharing Enum Caches. Review URL: https://chromiumcodereview.appspot.com/10824079 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12400 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 22 ++++++--- src/arm/lithium-arm.cc | 6 +++ src/arm/lithium-arm.h | 11 +++++ src/arm/lithium-codegen-arm.cc | 17 +++++++ src/arm/macro-assembler-arm.cc | 76 ++++++++++++++------------------ src/arm/macro-assembler-arm.h | 1 + src/handles.cc | 58 +++++++++++++++--------- src/heap.cc | 3 +- src/hydrogen-instructions.h | 21 +++++++++ src/hydrogen.cc | 5 +-- src/ia32/full-codegen-ia32.cc | 17 +++++-- src/ia32/lithium-codegen-ia32.cc | 16 +++++++ src/ia32/lithium-ia32.cc | 6 +++ src/ia32/lithium-ia32.h | 11 +++++ src/ia32/macro-assembler-ia32.cc | 56 +++++++++++------------ src/ia32/macro-assembler-ia32.h | 8 ++++ src/objects.cc | 12 +++-- src/objects.h | 20 +++++++-- src/utils.h | 1 + src/x64/full-codegen-x64.cc | 20 ++++++--- src/x64/lithium-codegen-x64.cc | 15 +++++++ src/x64/lithium-x64.cc | 6 +++ src/x64/lithium-x64.h | 11 +++++ src/x64/macro-assembler-x64.cc | 68 +++++++++++++--------------- src/x64/macro-assembler-x64.h | 9 ++++ 25 files changed, 335 insertions(+), 161 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index f2b195ec98..a26c596051 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1125,26 +1125,34 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // modification check. Otherwise, we got a fixed array, and we have // to do a slow check. Label fixed_array; - __ mov(r2, r0); - __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kMetaMapRootIndex); - __ cmp(r1, ip); + __ cmp(r2, ip); __ b(ne, &fixed_array); // We got a map in register r0. Get the enumeration cache from it. + Label no_descriptors; __ bind(&use_cache); - __ LoadInstanceDescriptors(r0, r1, r2); - __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumCacheOffset)); - __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); + + __ EnumLength(r1, r0); + __ cmp(r1, Operand(Smi::FromInt(0))); + __ b(eq, &no_descriptors); + + __ LoadInstanceDescriptors(r0, r2, r4); + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheOffset)); + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset)); // Set up the four remaining stack slots. __ push(r0); // Map. - __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); __ mov(r0, Operand(Smi::FromInt(0))); // Push enumeration cache, enumeration cache length (as smi) and zero. __ Push(r2, r1, r0); __ jmp(&loop); + __ bind(&no_descriptors); + __ Drop(1); + __ jmp(&exit); + // We got a fixed array in register r0. Iterate through that. Label non_proxy; __ bind(&fixed_array); diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 2ddabfc65b..aac7e8c324 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1539,6 +1539,12 @@ LInstruction* LChunkBuilder::DoFixedArrayBaseLength( } +LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) { + LOperand* map = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new(zone()) LMapEnumLength(map)); +} + + LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LOperand* object = UseRegisterAtStart(instr->value()); return DefineAsRegister(new(zone()) LElementsKind(object)); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 782dd61758..e6e102f762 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -132,6 +132,7 @@ class LCodeGen; V(LoadNamedField) \ V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ + V(MapEnumLength) \ V(MathFloorOfDiv) \ V(MathMinMax) \ V(ModI) \ @@ -1002,6 +1003,16 @@ class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { }; +class LMapEnumLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LMapEnumLength(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length") +}; + + class LElementsKind: public LTemplateInstruction<1, 1, 0> { public: explicit LElementsKind(LOperand* value) { diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index e2aa254edf..88169a4537 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -1539,6 +1539,13 @@ void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { } +void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) { + Register result = ToRegister(instr->result()); + Register map = ToRegister(instr->InputAt(0)); + __ EnumLength(result, map); +} + + void LCodeGen::DoElementsKind(LElementsKind* instr) { Register result = ToRegister(instr->result()); Register input = ToRegister(instr->InputAt(0)); @@ -5558,6 +5565,14 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { Register map = ToRegister(instr->map()); Register result = ToRegister(instr->result()); Register scratch = ToRegister(instr->scratch()); + Label load_cache, done; + __ EnumLength(result, map); + __ cmp(result, Operand(Smi::FromInt(0))); + __ b(ne, &load_cache); + __ mov(result, Operand(isolate()->factory()->empty_fixed_array())); + __ jmp(&done); + + __ bind(&load_cache); __ LoadInstanceDescriptors(map, result, scratch); __ ldr(result, FieldMemOperand(result, DescriptorArray::kEnumCacheOffset)); @@ -5565,6 +5580,8 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { FieldMemOperand(result, FixedArray::SizeFor(instr->idx()))); __ cmp(result, Operand(0)); DeoptimizeIf(eq, instr->environment()); + + __ bind(&done); } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 4d7198bff6..2a677be525 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -3717,55 +3717,47 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); + and_(dst, dst, Operand(Smi::FromInt(Map::EnumLengthBits::kMask))); +} + + void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { - Label next; - // Preload a couple of values used in the loop. Register empty_fixed_array_value = r6; LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - mov(r1, r0); - bind(&next); + Label next, start; + mov(r2, r0); - // Check that there are no elements. Register r1 contains the - // current JS object we've reached through the prototype chain. - ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. + ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); + + EnumLength(r3, r1); + cmp(r3, Operand(Smi::FromInt(Map::kInvalidEnumCache))); + b(eq, call_runtime); + + jmp(&start); + + bind(&next); + ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); + + // For all objects but the receiver, check that the cache is empty. + EnumLength(r3, r1); + cmp(r3, Operand(Smi::FromInt(0))); + b(ne, call_runtime); + + bind(&start); + + // Check that there are no elements. Register r2 contains the current JS + // object we've reached through the prototype chain. + ldr(r2, FieldMemOperand(r2, JSObject::kElementsOffset)); cmp(r2, empty_fixed_array_value); b(ne, call_runtime); - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in r2 for the subsequent - // prototype load. - ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - ldr(r3, FieldMemOperand(r2, Map::kTransitionsOrBackPointerOffset)); - - CheckMap(r3, - r7, - isolate()->factory()->fixed_array_map(), - call_runtime, - DONT_DO_SMI_CHECK); - - LoadRoot(r7, Heap::kEmptyDescriptorArrayRootIndex); - ldr(r3, FieldMemOperand(r3, TransitionArray::kDescriptorsOffset)); - cmp(r3, r7); - b(eq, call_runtime); - - // Check that there is an enum cache in the non-empty instance - // descriptors (r3). This is the case if the next enumeration - // index field does not contain a smi. - ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumCacheOffset)); - JumpIfSmi(r3, call_runtime); - - // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - cmp(r1, r0); - b(eq, &check_prototype); - ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumCacheBridgeCacheOffset)); - cmp(r3, empty_fixed_array_value); - b(ne, call_runtime); - - // Load the prototype from the map and loop if non-null. - bind(&check_prototype); - ldr(r1, FieldMemOperand(r2, Map::kPrototypeOffset)); - cmp(r1, null_value); + ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset)); + cmp(r2, null_value); b(ne, &next); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 6e12b671cd..8eb97125ea 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -1272,6 +1272,7 @@ class MacroAssembler: public Assembler { void LoadInstanceDescriptors(Register map, Register descriptors, Register scratch); + void EnumLength(Register dst, Register map); // Activation support. void EnterFrame(StackFrame::Type type); diff --git a/src/handles.cc b/src/handles.cc index 353a4346d3..adf435573e 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -704,30 +704,44 @@ Handle GetEnumPropertyKeys(Handle object, Isolate* isolate = object->GetIsolate(); if (object->HasFastProperties()) { if (object->map()->instance_descriptors()->HasEnumCache()) { - isolate->counters()->enum_cache_hits()->Increment(); + int own_property_count = object->map()->EnumLength(); + + // Mark that we have an enum cache if we are allowed to cache it. + if (cache_result && own_property_count == Map::kInvalidEnumCache) { + int num_enum = object->map()->NumberOfDescribedProperties(DONT_ENUM); + object->map()->SetEnumLength(num_enum); + } + DescriptorArray* desc = object->map()->instance_descriptors(); - return Handle(FixedArray::cast(desc->GetEnumCache()), - isolate); + Handle keys(FixedArray::cast(desc->GetEnumCache()), isolate); + + isolate->counters()->enum_cache_hits()->Increment(); + return keys; } - isolate->counters()->enum_cache_misses()->Increment(); + Handle map(object->map()); - int num_enum = object->NumberOfLocalProperties(DONT_ENUM); + + if (map->instance_descriptors()->IsEmpty()) { + isolate->counters()->enum_cache_hits()->Increment(); + if (cache_result) map->SetEnumLength(0); + return isolate->factory()->empty_fixed_array(); + } + + isolate->counters()->enum_cache_misses()->Increment(); + + int num_enum = map->NumberOfDescribedProperties(DONT_ENUM); Handle storage = isolate->factory()->NewFixedArray(num_enum); - Handle indices; - - if (cache_result) { - indices = isolate->factory()->NewFixedArray(num_enum); - } + Handle indices = isolate->factory()->NewFixedArray(num_enum); Handle descs = Handle(object->map()->instance_descriptors(), isolate); int index = 0; for (int i = 0; i < descs->number_of_descriptors(); i++) { - if (!descs->GetDetails(i).IsDontEnum()) { + PropertyDetails details = descs->GetDetails(i); + if (!details.IsDontEnum()) { storage->set(index, descs->GetKey(i)); - PropertyDetails details = descs->GetDetails(i); if (!indices.is_null()) { if (details.type() != FIELD) { indices = Handle(); @@ -742,17 +756,19 @@ Handle GetEnumPropertyKeys(Handle object, index++; } } + ASSERT(index == storage->length()); + + Handle bridge_storage = + isolate->factory()->NewFixedArray( + DescriptorArray::kEnumCacheBridgeLength); + DescriptorArray* desc = object->map()->instance_descriptors(); + desc->SetEnumCache(*bridge_storage, + *storage, + indices.is_null() ? Object::cast(Smi::FromInt(0)) + : Object::cast(*indices)); if (cache_result) { - Handle bridge_storage = - isolate->factory()->NewFixedArray( - DescriptorArray::kEnumCacheBridgeLength); - DescriptorArray* desc = object->map()->instance_descriptors(); - desc->SetEnumCache(*bridge_storage, - *storage, - indices.is_null() ? Object::cast(Smi::FromInt(0)) - : Object::cast(*indices)); + object->map()->SetEnumLength(index); } - ASSERT(storage->length() == index); return storage; } else { int num_enum = object->NumberOfLocalProperties(DONT_ENUM); diff --git a/src/heap.cc b/src/heap.cc index 9ef8a60d9c..b2174f5325 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2090,7 +2090,8 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, map->set_unused_property_fields(0); map->set_bit_field(0); map->set_bit_field2(1 << Map::kIsExtensible); - map->set_bit_field3(0); + int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache); + map->set_bit_field3(bit_field3); map->set_elements_kind(elements_kind); // If the map object is aligned fill the padding area with Smi 0 objects. diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 4a16288943..b5741c633b 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -139,6 +139,7 @@ class LChunkBuilder; V(LoadNamedField) \ V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ + V(MapEnumLength) \ V(MathFloorOfDiv) \ V(MathMinMax) \ V(Mod) \ @@ -1923,6 +1924,26 @@ class HFixedArrayBaseLength: public HUnaryOperation { }; +class HMapEnumLength: public HUnaryOperation { + public: + explicit HMapEnumLength(HValue* value) : HUnaryOperation(value) { + set_type(HType::Smi()); + set_representation(Representation::Tagged()); + SetFlag(kUseGVN); + SetGVNFlag(kDependsOnMaps); + } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(MapEnumLength) + + protected: + virtual bool DataEquals(HValue* other) { return true; } +}; + + class HElementsKind: public HUnaryOperation { public: explicit HElementsKind(HValue* value) : HUnaryOperation(value) { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 68cd89f169..0c223501b7 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -4582,15 +4582,14 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) { map, DescriptorArray::kEnumCacheBridgeCacheIndex)); - HInstruction* array_length = AddInstruction( - new(zone()) HFixedArrayBaseLength(array)); + HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map)); HInstruction* start_index = AddInstruction(new(zone()) HConstant( Handle(Smi::FromInt(0)), Representation::Integer32())); Push(map); Push(array); - Push(array_length); + Push(enum_length); Push(start_index); HInstruction* index_cache = AddInstruction( diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 9ba5f87603..64400de81d 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1091,19 +1091,28 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // We got a map in register eax. Get the enumeration cache from it. + Label no_descriptors; __ bind(&use_cache); + + __ EnumLength(edx, eax); + __ cmp(edx, Immediate(Smi::FromInt(0))); + __ j(equal, &no_descriptors); + __ LoadInstanceDescriptors(eax, ecx); __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheOffset)); - __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); // Set up the four remaining stack slots. __ push(eax); // Map. - __ push(edx); // Enumeration cache. - __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset)); - __ push(eax); // Enumeration cache length (as smi). + __ push(ecx); // Enumeration cache. + __ push(edx); // Number of valid entries for the map in the enum cache. __ push(Immediate(Smi::FromInt(0))); // Initial index. __ jmp(&loop); + __ bind(&no_descriptors); + __ add(esp, Immediate(kPointerSize)); + __ jmp(&exit); + // We got a fixed array in register eax. Iterate through that. Label non_proxy; __ bind(&fixed_array); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 5ef0739145..153e335946 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -1394,6 +1394,13 @@ void LCodeGen::DoFixedArrayBaseLength( } +void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) { + Register result = ToRegister(instr->result()); + Register map = ToRegister(instr->InputAt(0)); + __ EnumLength(result, map); +} + + void LCodeGen::DoElementsKind(LElementsKind* instr) { Register result = ToRegister(instr->result()); Register input = ToRegister(instr->InputAt(0)); @@ -5454,11 +5461,20 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) { void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { Register map = ToRegister(instr->map()); Register result = ToRegister(instr->result()); + Label load_cache, done; + __ EnumLength(result, map); + __ cmp(result, Immediate(Smi::FromInt(0))); + __ j(not_equal, &load_cache); + __ mov(result, isolate()->factory()->empty_fixed_array()); + __ jmp(&done); + + __ bind(&load_cache); __ LoadInstanceDescriptors(map, result); __ mov(result, FieldOperand(result, DescriptorArray::kEnumCacheOffset)); __ mov(result, FieldOperand(result, FixedArray::SizeFor(instr->idx()))); + __ bind(&done); __ test(result, result); DeoptimizeIf(equal, instr->environment()); } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 3f72a9edff..66da00ddca 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1587,6 +1587,12 @@ LInstruction* LChunkBuilder::DoFixedArrayBaseLength( } +LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) { + LOperand* map = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new(zone()) LMapEnumLength(map)); +} + + LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LOperand* object = UseRegisterAtStart(instr->value()); return DefineAsRegister(new(zone()) LElementsKind(object)); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index be1940baa3..83a6c6f32b 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -126,6 +126,7 @@ class LCodeGen; V(LoadNamedField) \ V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ + V(MapEnumLength) \ V(MathFloorOfDiv) \ V(MathMinMax) \ V(MathPowHalf) \ @@ -1019,6 +1020,16 @@ class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { }; +class LMapEnumLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LMapEnumLength(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length") +}; + + class LElementsKind: public LTemplateInstruction<1, 1, 0> { public: explicit LElementsKind(LOperand* value) { diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 85dcf1f743..7069f98e13 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -2897,47 +2897,43 @@ void MacroAssembler::EnsureNotWhite( } +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + mov(dst, FieldOperand(map, Map::kBitField3Offset)); + and_(dst, Immediate(Smi::FromInt(Map::EnumLengthBits::kMask))); +} + + void MacroAssembler::CheckEnumCache(Label* call_runtime) { - Label next; + Label next, start; mov(ecx, eax); - bind(&next); - // Check that there are no elements. Register ecx contains the - // current JS object we've reached through the prototype chain. - cmp(FieldOperand(ecx, JSObject::kElementsOffset), - isolate()->factory()->empty_fixed_array()); - j(not_equal, call_runtime); - - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in ebx for the subsequent - // prototype load. + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); - mov(edx, FieldOperand(ebx, Map::kTransitionsOrBackPointerOffset)); - CheckMap(edx, - isolate()->factory()->fixed_array_map(), - call_runtime, - DONT_DO_SMI_CHECK); - mov(edx, FieldOperand(edx, TransitionArray::kDescriptorsOffset)); - cmp(edx, isolate()->factory()->empty_descriptor_array()); + EnumLength(edx, ebx); + cmp(edx, Immediate(Smi::FromInt(Map::kInvalidEnumCache))); j(equal, call_runtime); - // Check that there is an enum cache in the non-empty instance - // descriptors (edx). This is the case if the next enumeration - // index field does not contain a smi. - mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheOffset)); - JumpIfSmi(edx, call_runtime); + jmp(&start); + + bind(&next); + mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - cmp(ecx, eax); - j(equal, &check_prototype, Label::kNear); - mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - cmp(edx, isolate()->factory()->empty_fixed_array()); + EnumLength(edx, ebx); + cmp(edx, Immediate(Smi::FromInt(0))); + j(not_equal, call_runtime); + + bind(&start); + + // Check that there are no elements. Register rcx contains the current JS + // object we've reached through the prototype chain. + mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); + cmp(ecx, isolate()->factory()->empty_fixed_array()); j(not_equal, call_runtime); - // Load the prototype from the map and loop if non-null. - bind(&check_prototype); mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset)); cmp(ecx, isolate()->factory()->null_value()); j(not_equal, &next); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 8ec3652deb..7d475e7d7e 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -492,7 +492,15 @@ class MacroAssembler: public Assembler { } void LoadInstanceDescriptors(Register map, Register descriptors); + void EnumLength(Register dst, Register map); + template + void DecodeField(Register reg) { + static const int full_shift = Field::kShift + kSmiTagSize; + static const int low_mask = Field::kMask >> Field::kShift; + sar(reg, full_shift); + and_(reg, Immediate(low_mask)); + } void LoadPowerOf2(XMMRegister dst, Register scratch, int power); // Abort execution if argument is not a number. Used in debug code. diff --git a/src/objects.cc b/src/objects.cc index 2fea4849ad..5bd423e157 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4149,16 +4149,13 @@ bool JSReceiver::IsSimpleEnum() { o = JSObject::cast(o)->GetPrototype()) { if (!o->IsJSObject()) return false; JSObject* curr = JSObject::cast(o); - if (!curr->map()->instance_descriptors()->HasEnumCache()) return false; + int enum_length = curr->map()->EnumLength(); + if (enum_length == Map::kInvalidEnumCache) return false; ASSERT(!curr->HasNamedInterceptor()); ASSERT(!curr->HasIndexedInterceptor()); ASSERT(!curr->IsAccessCheckNeeded()); if (curr->NumberOfEnumElements() > 0) return false; - if (curr != this) { - FixedArray* curr_fixed_array = - FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache()); - if (curr_fixed_array->length() > 0) return false; - } + if (curr != this && enum_length != 0) return false; } return true; } @@ -4849,6 +4846,7 @@ MaybeObject* Map::RawCopy(int instance_size) { result->set_bit_field2(bit_field2()); result->set_bit_field3(bit_field3()); result->SetNumberOfOwnDescriptors(0); + result->SetEnumLength(kInvalidEnumCache); return result; } @@ -5775,7 +5773,7 @@ MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) { MaybeObject* maybe_result = accessor->AddElementsToFixedArray(NULL, NULL, this, other); FixedArray* result; - if (!maybe_result->To(&result)) return maybe_result; + if (!maybe_result->To(&result)) return maybe_result; #ifdef DEBUG if (FLAG_enable_slow_asserts) { for (int i = 0; i < result->length(); i++) { diff --git a/src/objects.h b/src/objects.h index 64a8f3cd3d..5b5a3285b6 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4663,10 +4663,11 @@ class Map: public HeapObject { inline int bit_field3(); inline void set_bit_field3(int value); - class NumberOfOwnDescriptorsBits: public BitField {}; - class IsShared: public BitField {}; - class FunctionWithPrototype: public BitField {}; - class DictionaryMap: public BitField {}; + class EnumLengthBits: public BitField {}; + class NumberOfOwnDescriptorsBits: public BitField {}; + class IsShared: public BitField {}; + class FunctionWithPrototype: public BitField {}; + class DictionaryMap: public BitField {}; // Tells whether the object in the prototype property will be used // for instances created from this function. If the prototype @@ -4918,6 +4919,14 @@ class Map: public HeapObject { set_bit_field3(NumberOfOwnDescriptorsBits::update(bit_field3(), number)); } + int EnumLength() { + return EnumLengthBits::decode(bit_field3()); + } + + void SetEnumLength(int index) { + set_bit_field3(EnumLengthBits::update(bit_field3(), index)); + } + MUST_USE_RESULT MaybeObject* RawCopy(int instance_size); MUST_USE_RESULT MaybeObject* CopyWithPreallocatedFieldDescriptors(); MUST_USE_RESULT MaybeObject* CopyDropDescriptors(); @@ -5057,6 +5066,9 @@ class Map: public HeapObject { static const int kMaxPreAllocatedPropertyFields = 255; + // Constant for denoting that the Enum Cache field was not yet used. + static const int kInvalidEnumCache = EnumLengthBits::kMax; + // Layout description. static const int kInstanceSizesOffset = HeapObject::kHeaderSize; static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize; diff --git a/src/utils.h b/src/utils.h index 3b49a7e713..dc3a171c8d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -248,6 +248,7 @@ class BitField { // bitfield without compiler warnings we have to compute 2^32 without // using a shift count of 32. static const uint32_t kMask = ((1U << shift) << size) - (1U << shift); + static const uint32_t kShift = shift; // Value for the field with all bits set. static const T kMax = static_cast((1U << size) - 1); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index fae74603ce..acef06c9c3 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1105,22 +1105,32 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { Label fixed_array; __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), Heap::kMetaMapRootIndex); - __ j(not_equal, &fixed_array, Label::kNear); + __ j(not_equal, &fixed_array); // We got a map in register rax. Get the enumeration cache from it. __ bind(&use_cache); + + Label no_descriptors; + + __ EnumLength(rdx, rax); + __ Cmp(rdx, Smi::FromInt(0)); + __ j(equal, &no_descriptors); + __ LoadInstanceDescriptors(rax, rcx); __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheOffset)); - __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); // Set up the four remaining stack slots. __ push(rax); // Map. - __ push(rdx); // Enumeration cache. - __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); - __ push(rax); // Enumeration cache length (as smi). + __ push(rcx); // Enumeration cache. + __ push(rdx); // Number of valid entries for the map in the enum cache. __ Push(Smi::FromInt(0)); // Initial index. __ jmp(&loop); + __ bind(&no_descriptors); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(&exit); + // We got a fixed array in register rax. Iterate through that. Label non_proxy; __ bind(&fixed_array); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index df2dd859aa..27bfe15e33 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -1285,6 +1285,13 @@ void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { } +void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) { + Register result = ToRegister(instr->result()); + Register map = ToRegister(instr->InputAt(0)); + __ EnumLength(result, map); +} + + void LCodeGen::DoElementsKind(LElementsKind* instr) { Register result = ToRegister(instr->result()); Register input = ToRegister(instr->InputAt(0)); @@ -5217,11 +5224,19 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) { void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { Register map = ToRegister(instr->map()); Register result = ToRegister(instr->result()); + Label load_cache, done; + __ EnumLength(result, map); + __ Cmp(result, Smi::FromInt(0)); + __ j(not_equal, &load_cache); + __ LoadRoot(result, Heap::kEmptyFixedArrayRootIndex); + __ jmp(&done); + __ bind(&load_cache); __ LoadInstanceDescriptors(map, result); __ movq(result, FieldOperand(result, DescriptorArray::kEnumCacheOffset)); __ movq(result, FieldOperand(result, FixedArray::SizeFor(instr->idx()))); + __ bind(&done); Condition cc = masm()->CheckSmi(result); DeoptimizeIf(cc, instr->environment()); } diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index bc6eb3d135..af2dcc24f9 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1528,6 +1528,12 @@ LInstruction* LChunkBuilder::DoFixedArrayBaseLength( } +LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) { + LOperand* map = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new(zone()) LMapEnumLength(map)); +} + + LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LOperand* object = UseRegisterAtStart(instr->value()); return DefineAsRegister(new(zone()) LElementsKind(object)); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 449c2a129d..84d05c051a 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -96,6 +96,7 @@ class LCodeGen; V(ElementsKind) \ V(FastLiteral) \ V(FixedArrayBaseLength) \ + V(MapEnumLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ V(GlobalObject) \ @@ -1002,6 +1003,16 @@ class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { }; +class LMapEnumLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LMapEnumLength(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length") +}; + + class LElementsKind: public LTemplateInstruction<1, 1, 0> { public: explicit LElementsKind(LOperand* value) { diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index d49048f264..32a92a6c65 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2898,6 +2898,14 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + movq(dst, FieldOperand(map, Map::kBitField3Offset)); + Move(kScratchRegister, Smi::FromInt(Map::EnumLengthBits::kMask)); + and_(dst, kScratchRegister); +} + + void MacroAssembler::DispatchMap(Register obj, Handle map, Handle success, @@ -4479,52 +4487,38 @@ void MacroAssembler::EnsureNotWhite( void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { - Label next; + Label next, start; Register empty_fixed_array_value = r8; LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Register empty_descriptor_array_value = r9; - LoadRoot(empty_descriptor_array_value, - Heap::kEmptyDescriptorArrayRootIndex); movq(rcx, rax); + + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. + movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); + + EnumLength(rdx, rbx); + Cmp(rdx, Smi::FromInt(Map::kInvalidEnumCache)); + j(equal, call_runtime); + + jmp(&start); + bind(&next); - // Check that there are no elements. Register rcx contains the - // current JS object we've reached through the prototype chain. + movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); + + // For all objects but the receiver, check that the cache is empty. + EnumLength(rdx, rbx); + Cmp(rdx, Smi::FromInt(0)); + j(not_equal, call_runtime); + + bind(&start); + + // Check that there are no elements. Register rcx contains the current JS + // object we've reached through the prototype chain. cmpq(empty_fixed_array_value, FieldOperand(rcx, JSObject::kElementsOffset)); j(not_equal, call_runtime); - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in rbx for the subsequent - // prototype load. - movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); - movq(rdx, FieldOperand(rbx, Map::kTransitionsOrBackPointerOffset)); - - CheckMap(rdx, - isolate()->factory()->fixed_array_map(), - call_runtime, - DONT_DO_SMI_CHECK); - - movq(rdx, FieldOperand(rdx, TransitionArray::kDescriptorsOffset)); - cmpq(rdx, empty_descriptor_array_value); - j(equal, call_runtime); - - // Check that there is an enum cache in the non-empty instance - // descriptors (rdx). This is the case if the next enumeration - // index field does not contain a smi. - movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheOffset)); - JumpIfSmi(rdx, call_runtime); - - // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - cmpq(rcx, rax); - j(equal, &check_prototype, Label::kNear); - movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - cmpq(rdx, empty_fixed_array_value); - j(not_equal, call_runtime); - - // Load the prototype from the map and loop if non-null. - bind(&check_prototype); movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); cmpq(rcx, null_value); j(not_equal, &next); diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 9c8f550f34..5268fe2a2e 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -948,6 +948,15 @@ class MacroAssembler: public Assembler { void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch); void LoadInstanceDescriptors(Register map, Register descriptors); + void EnumLength(Register dst, Register map); + + template + void DecodeField(Register reg) { + static const int full_shift = Field::kShift + kSmiShift; + static const int low_mask = Field::kMask >> Field::kShift; + shr(reg, Immediate(full_shift)); + and_(reg, Immediate(low_mask)); + } // Abort execution if argument is not a number. Used in debug code. void AbortIfNotNumber(Register object);