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
This commit is contained in:
verwaest@chromium.org 2012-08-28 14:20:50 +00:00
parent b0067e9cd5
commit 78037d0a4e
25 changed files with 335 additions and 161 deletions

View File

@ -1125,26 +1125,34 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// modification check. Otherwise, we got a fixed array, and we have // modification check. Otherwise, we got a fixed array, and we have
// to do a slow check. // to do a slow check.
Label fixed_array; Label fixed_array;
__ mov(r2, r0); __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kMetaMapRootIndex); __ LoadRoot(ip, Heap::kMetaMapRootIndex);
__ cmp(r1, ip); __ cmp(r2, ip);
__ b(ne, &fixed_array); __ b(ne, &fixed_array);
// We got a map in register r0. Get the enumeration cache from it. // We got a map in register r0. Get the enumeration cache from it.
Label no_descriptors;
__ bind(&use_cache); __ bind(&use_cache);
__ LoadInstanceDescriptors(r0, r1, r2);
__ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumCacheOffset)); __ EnumLength(r1, r0);
__ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); __ 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. // Set up the four remaining stack slots.
__ push(r0); // Map. __ push(r0); // Map.
__ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset));
__ mov(r0, Operand(Smi::FromInt(0))); __ mov(r0, Operand(Smi::FromInt(0)));
// Push enumeration cache, enumeration cache length (as smi) and zero. // Push enumeration cache, enumeration cache length (as smi) and zero.
__ Push(r2, r1, r0); __ Push(r2, r1, r0);
__ jmp(&loop); __ jmp(&loop);
__ bind(&no_descriptors);
__ Drop(1);
__ jmp(&exit);
// We got a fixed array in register r0. Iterate through that. // We got a fixed array in register r0. Iterate through that.
Label non_proxy; Label non_proxy;
__ bind(&fixed_array); __ bind(&fixed_array);

View File

@ -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) { LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
LOperand* object = UseRegisterAtStart(instr->value()); LOperand* object = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LElementsKind(object)); return DefineAsRegister(new(zone()) LElementsKind(object));

View File

@ -132,6 +132,7 @@ class LCodeGen;
V(LoadNamedField) \ V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \ V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \ V(LoadNamedGeneric) \
V(MapEnumLength) \
V(MathFloorOfDiv) \ V(MathFloorOfDiv) \
V(MathMinMax) \ V(MathMinMax) \
V(ModI) \ 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> { class LElementsKind: public LTemplateInstruction<1, 1, 0> {
public: public:
explicit LElementsKind(LOperand* value) { explicit LElementsKind(LOperand* value) {

View File

@ -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) { void LCodeGen::DoElementsKind(LElementsKind* instr) {
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
@ -5558,6 +5565,14 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
Register map = ToRegister(instr->map()); Register map = ToRegister(instr->map());
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
Register scratch = ToRegister(instr->scratch()); 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); __ LoadInstanceDescriptors(map, result, scratch);
__ ldr(result, __ ldr(result,
FieldMemOperand(result, DescriptorArray::kEnumCacheOffset)); FieldMemOperand(result, DescriptorArray::kEnumCacheOffset));
@ -5565,6 +5580,8 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
FieldMemOperand(result, FixedArray::SizeFor(instr->idx()))); FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
__ cmp(result, Operand(0)); __ cmp(result, Operand(0));
DeoptimizeIf(eq, instr->environment()); DeoptimizeIf(eq, instr->environment());
__ bind(&done);
} }

View File

@ -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) { 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; Register empty_fixed_array_value = r6;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
mov(r1, r0); Label next, start;
bind(&next); mov(r2, r0);
// Check that there are no elements. Register r1 contains the // Check if the enum length field is properly initialized, indicating that
// current JS object we've reached through the prototype chain. // there is an enum cache.
ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); 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); cmp(r2, empty_fixed_array_value);
b(ne, call_runtime); b(ne, call_runtime);
// Check that instance descriptors are not empty so that we can ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
// check for an enum cache. Leave the map in r2 for the subsequent cmp(r2, null_value);
// 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);
b(ne, &next); b(ne, &next);
} }

View File

@ -1272,6 +1272,7 @@ class MacroAssembler: public Assembler {
void LoadInstanceDescriptors(Register map, void LoadInstanceDescriptors(Register map,
Register descriptors, Register descriptors,
Register scratch); Register scratch);
void EnumLength(Register dst, Register map);
// Activation support. // Activation support.
void EnterFrame(StackFrame::Type type); void EnterFrame(StackFrame::Type type);

View File

@ -704,30 +704,44 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
if (object->map()->instance_descriptors()->HasEnumCache()) { 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(); DescriptorArray* desc = object->map()->instance_descriptors();
return Handle<FixedArray>(FixedArray::cast(desc->GetEnumCache()), Handle<FixedArray> keys(FixedArray::cast(desc->GetEnumCache()), isolate);
isolate);
isolate->counters()->enum_cache_hits()->Increment();
return keys;
} }
isolate->counters()->enum_cache_misses()->Increment();
Handle<Map> map(object->map()); Handle<Map> 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<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum); Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum);
Handle<FixedArray> indices; Handle<FixedArray> indices = isolate->factory()->NewFixedArray(num_enum);
if (cache_result) {
indices = isolate->factory()->NewFixedArray(num_enum);
}
Handle<DescriptorArray> descs = Handle<DescriptorArray> descs =
Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate); Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
int index = 0; int index = 0;
for (int i = 0; i < descs->number_of_descriptors(); i++) { 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)); storage->set(index, descs->GetKey(i));
PropertyDetails details = descs->GetDetails(i);
if (!indices.is_null()) { if (!indices.is_null()) {
if (details.type() != FIELD) { if (details.type() != FIELD) {
indices = Handle<FixedArray>(); indices = Handle<FixedArray>();
@ -742,17 +756,19 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
index++; index++;
} }
} }
ASSERT(index == storage->length());
Handle<FixedArray> 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) { if (cache_result) {
Handle<FixedArray> bridge_storage = object->map()->SetEnumLength(index);
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));
} }
ASSERT(storage->length() == index);
return storage; return storage;
} else { } else {
int num_enum = object->NumberOfLocalProperties(DONT_ENUM); int num_enum = object->NumberOfLocalProperties(DONT_ENUM);

View File

@ -2090,7 +2090,8 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type,
map->set_unused_property_fields(0); map->set_unused_property_fields(0);
map->set_bit_field(0); map->set_bit_field(0);
map->set_bit_field2(1 << Map::kIsExtensible); 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); map->set_elements_kind(elements_kind);
// If the map object is aligned fill the padding area with Smi 0 objects. // If the map object is aligned fill the padding area with Smi 0 objects.

View File

@ -139,6 +139,7 @@ class LChunkBuilder;
V(LoadNamedField) \ V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \ V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \ V(LoadNamedGeneric) \
V(MapEnumLength) \
V(MathFloorOfDiv) \ V(MathFloorOfDiv) \
V(MathMinMax) \ V(MathMinMax) \
V(Mod) \ 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 { class HElementsKind: public HUnaryOperation {
public: public:
explicit HElementsKind(HValue* value) : HUnaryOperation(value) { explicit HElementsKind(HValue* value) : HUnaryOperation(value) {

View File

@ -4582,15 +4582,14 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
map, map,
DescriptorArray::kEnumCacheBridgeCacheIndex)); DescriptorArray::kEnumCacheBridgeCacheIndex));
HInstruction* array_length = AddInstruction( HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map));
new(zone()) HFixedArrayBaseLength(array));
HInstruction* start_index = AddInstruction(new(zone()) HConstant( HInstruction* start_index = AddInstruction(new(zone()) HConstant(
Handle<Object>(Smi::FromInt(0)), Representation::Integer32())); Handle<Object>(Smi::FromInt(0)), Representation::Integer32()));
Push(map); Push(map);
Push(array); Push(array);
Push(array_length); Push(enum_length);
Push(start_index); Push(start_index);
HInstruction* index_cache = AddInstruction( HInstruction* index_cache = AddInstruction(

View File

@ -1091,19 +1091,28 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// We got a map in register eax. Get the enumeration cache from it. // We got a map in register eax. Get the enumeration cache from it.
Label no_descriptors;
__ bind(&use_cache); __ bind(&use_cache);
__ EnumLength(edx, eax);
__ cmp(edx, Immediate(Smi::FromInt(0)));
__ j(equal, &no_descriptors);
__ LoadInstanceDescriptors(eax, ecx); __ LoadInstanceDescriptors(eax, ecx);
__ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheOffset)); __ 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. // Set up the four remaining stack slots.
__ push(eax); // Map. __ push(eax); // Map.
__ push(edx); // Enumeration cache. __ push(ecx); // Enumeration cache.
__ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset)); __ push(edx); // Number of valid entries for the map in the enum cache.
__ push(eax); // Enumeration cache length (as smi).
__ push(Immediate(Smi::FromInt(0))); // Initial index. __ push(Immediate(Smi::FromInt(0))); // Initial index.
__ jmp(&loop); __ jmp(&loop);
__ bind(&no_descriptors);
__ add(esp, Immediate(kPointerSize));
__ jmp(&exit);
// We got a fixed array in register eax. Iterate through that. // We got a fixed array in register eax. Iterate through that.
Label non_proxy; Label non_proxy;
__ bind(&fixed_array); __ bind(&fixed_array);

View File

@ -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) { void LCodeGen::DoElementsKind(LElementsKind* instr) {
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
@ -5454,11 +5461,20 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
Register map = ToRegister(instr->map()); Register map = ToRegister(instr->map());
Register result = ToRegister(instr->result()); 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); __ LoadInstanceDescriptors(map, result);
__ mov(result, __ mov(result,
FieldOperand(result, DescriptorArray::kEnumCacheOffset)); FieldOperand(result, DescriptorArray::kEnumCacheOffset));
__ mov(result, __ mov(result,
FieldOperand(result, FixedArray::SizeFor(instr->idx()))); FieldOperand(result, FixedArray::SizeFor(instr->idx())));
__ bind(&done);
__ test(result, result); __ test(result, result);
DeoptimizeIf(equal, instr->environment()); DeoptimizeIf(equal, instr->environment());
} }

View File

@ -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) { LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
LOperand* object = UseRegisterAtStart(instr->value()); LOperand* object = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LElementsKind(object)); return DefineAsRegister(new(zone()) LElementsKind(object));

View File

@ -126,6 +126,7 @@ class LCodeGen;
V(LoadNamedField) \ V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \ V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \ V(LoadNamedGeneric) \
V(MapEnumLength) \
V(MathFloorOfDiv) \ V(MathFloorOfDiv) \
V(MathMinMax) \ V(MathMinMax) \
V(MathPowHalf) \ 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> { class LElementsKind: public LTemplateInstruction<1, 1, 0> {
public: public:
explicit LElementsKind(LOperand* value) { explicit LElementsKind(LOperand* value) {

View File

@ -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) { void MacroAssembler::CheckEnumCache(Label* call_runtime) {
Label next; Label next, start;
mov(ecx, eax); mov(ecx, eax);
bind(&next);
// Check that there are no elements. Register ecx contains the // Check if the enum length field is properly initialized, indicating that
// current JS object we've reached through the prototype chain. // there is an enum cache.
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.
mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); 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)); EnumLength(edx, ebx);
cmp(edx, isolate()->factory()->empty_descriptor_array()); cmp(edx, Immediate(Smi::FromInt(Map::kInvalidEnumCache)));
j(equal, call_runtime); j(equal, call_runtime);
// Check that there is an enum cache in the non-empty instance jmp(&start);
// descriptors (edx). This is the case if the next enumeration
// index field does not contain a smi. bind(&next);
mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheOffset)); mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
JumpIfSmi(edx, call_runtime);
// For all objects but the receiver, check that the cache is empty. // For all objects but the receiver, check that the cache is empty.
Label check_prototype; EnumLength(edx, ebx);
cmp(ecx, eax); cmp(edx, Immediate(Smi::FromInt(0)));
j(equal, &check_prototype, Label::kNear); j(not_equal, call_runtime);
mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
cmp(edx, isolate()->factory()->empty_fixed_array()); 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); 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)); mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
cmp(ecx, isolate()->factory()->null_value()); cmp(ecx, isolate()->factory()->null_value());
j(not_equal, &next); j(not_equal, &next);

View File

@ -492,7 +492,15 @@ class MacroAssembler: public Assembler {
} }
void LoadInstanceDescriptors(Register map, Register descriptors); void LoadInstanceDescriptors(Register map, Register descriptors);
void EnumLength(Register dst, Register map);
template<typename Field>
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); void LoadPowerOf2(XMMRegister dst, Register scratch, int power);
// Abort execution if argument is not a number. Used in debug code. // Abort execution if argument is not a number. Used in debug code.

View File

@ -4149,16 +4149,13 @@ bool JSReceiver::IsSimpleEnum() {
o = JSObject::cast(o)->GetPrototype()) { o = JSObject::cast(o)->GetPrototype()) {
if (!o->IsJSObject()) return false; if (!o->IsJSObject()) return false;
JSObject* curr = JSObject::cast(o); 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->HasNamedInterceptor());
ASSERT(!curr->HasIndexedInterceptor()); ASSERT(!curr->HasIndexedInterceptor());
ASSERT(!curr->IsAccessCheckNeeded()); ASSERT(!curr->IsAccessCheckNeeded());
if (curr->NumberOfEnumElements() > 0) return false; if (curr->NumberOfEnumElements() > 0) return false;
if (curr != this) { if (curr != this && enum_length != 0) return false;
FixedArray* curr_fixed_array =
FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
if (curr_fixed_array->length() > 0) return false;
}
} }
return true; return true;
} }
@ -4849,6 +4846,7 @@ MaybeObject* Map::RawCopy(int instance_size) {
result->set_bit_field2(bit_field2()); result->set_bit_field2(bit_field2());
result->set_bit_field3(bit_field3()); result->set_bit_field3(bit_field3());
result->SetNumberOfOwnDescriptors(0); result->SetNumberOfOwnDescriptors(0);
result->SetEnumLength(kInvalidEnumCache);
return result; return result;
} }
@ -5775,7 +5773,7 @@ MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
MaybeObject* maybe_result = MaybeObject* maybe_result =
accessor->AddElementsToFixedArray(NULL, NULL, this, other); accessor->AddElementsToFixedArray(NULL, NULL, this, other);
FixedArray* result; FixedArray* result;
if (!maybe_result->To<FixedArray>(&result)) return maybe_result; if (!maybe_result->To(&result)) return maybe_result;
#ifdef DEBUG #ifdef DEBUG
if (FLAG_enable_slow_asserts) { if (FLAG_enable_slow_asserts) {
for (int i = 0; i < result->length(); i++) { for (int i = 0; i < result->length(); i++) {

View File

@ -4663,10 +4663,11 @@ class Map: public HeapObject {
inline int bit_field3(); inline int bit_field3();
inline void set_bit_field3(int value); inline void set_bit_field3(int value);
class NumberOfOwnDescriptorsBits: public BitField<int, 0, 11> {}; class EnumLengthBits: public BitField<int, 0, 11> {};
class IsShared: public BitField<bool, 11, 1> {}; class NumberOfOwnDescriptorsBits: public BitField<int, 11, 11> {};
class FunctionWithPrototype: public BitField<bool, 12, 1> {}; class IsShared: public BitField<bool, 22, 1> {};
class DictionaryMap: public BitField<bool, 13, 1> {}; class FunctionWithPrototype: public BitField<bool, 23, 1> {};
class DictionaryMap: public BitField<bool, 24, 1> {};
// Tells whether the object in the prototype property will be used // Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype // 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)); 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* RawCopy(int instance_size);
MUST_USE_RESULT MaybeObject* CopyWithPreallocatedFieldDescriptors(); MUST_USE_RESULT MaybeObject* CopyWithPreallocatedFieldDescriptors();
MUST_USE_RESULT MaybeObject* CopyDropDescriptors(); MUST_USE_RESULT MaybeObject* CopyDropDescriptors();
@ -5057,6 +5066,9 @@ class Map: public HeapObject {
static const int kMaxPreAllocatedPropertyFields = 255; 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. // Layout description.
static const int kInstanceSizesOffset = HeapObject::kHeaderSize; static const int kInstanceSizesOffset = HeapObject::kHeaderSize;
static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize; static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize;

View File

@ -248,6 +248,7 @@ class BitField {
// bitfield without compiler warnings we have to compute 2^32 without // bitfield without compiler warnings we have to compute 2^32 without
// using a shift count of 32. // using a shift count of 32.
static const uint32_t kMask = ((1U << shift) << size) - (1U << shift); static const uint32_t kMask = ((1U << shift) << size) - (1U << shift);
static const uint32_t kShift = shift;
// Value for the field with all bits set. // Value for the field with all bits set.
static const T kMax = static_cast<T>((1U << size) - 1); static const T kMax = static_cast<T>((1U << size) - 1);

View File

@ -1105,22 +1105,32 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
Label fixed_array; Label fixed_array;
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kMetaMapRootIndex); 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. // We got a map in register rax. Get the enumeration cache from it.
__ bind(&use_cache); __ bind(&use_cache);
Label no_descriptors;
__ EnumLength(rdx, rax);
__ Cmp(rdx, Smi::FromInt(0));
__ j(equal, &no_descriptors);
__ LoadInstanceDescriptors(rax, rcx); __ LoadInstanceDescriptors(rax, rcx);
__ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheOffset)); __ 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. // Set up the four remaining stack slots.
__ push(rax); // Map. __ push(rax); // Map.
__ push(rdx); // Enumeration cache. __ push(rcx); // Enumeration cache.
__ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); __ push(rdx); // Number of valid entries for the map in the enum cache.
__ push(rax); // Enumeration cache length (as smi).
__ Push(Smi::FromInt(0)); // Initial index. __ Push(Smi::FromInt(0)); // Initial index.
__ jmp(&loop); __ jmp(&loop);
__ bind(&no_descriptors);
__ addq(rsp, Immediate(kPointerSize));
__ jmp(&exit);
// We got a fixed array in register rax. Iterate through that. // We got a fixed array in register rax. Iterate through that.
Label non_proxy; Label non_proxy;
__ bind(&fixed_array); __ bind(&fixed_array);

View File

@ -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) { void LCodeGen::DoElementsKind(LElementsKind* instr) {
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
@ -5217,11 +5224,19 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
Register map = ToRegister(instr->map()); Register map = ToRegister(instr->map());
Register result = ToRegister(instr->result()); 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); __ LoadInstanceDescriptors(map, result);
__ movq(result, __ movq(result,
FieldOperand(result, DescriptorArray::kEnumCacheOffset)); FieldOperand(result, DescriptorArray::kEnumCacheOffset));
__ movq(result, __ movq(result,
FieldOperand(result, FixedArray::SizeFor(instr->idx()))); FieldOperand(result, FixedArray::SizeFor(instr->idx())));
__ bind(&done);
Condition cc = masm()->CheckSmi(result); Condition cc = masm()->CheckSmi(result);
DeoptimizeIf(cc, instr->environment()); DeoptimizeIf(cc, instr->environment());
} }

View File

@ -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) { LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
LOperand* object = UseRegisterAtStart(instr->value()); LOperand* object = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LElementsKind(object)); return DefineAsRegister(new(zone()) LElementsKind(object));

View File

@ -96,6 +96,7 @@ class LCodeGen;
V(ElementsKind) \ V(ElementsKind) \
V(FastLiteral) \ V(FastLiteral) \
V(FixedArrayBaseLength) \ V(FixedArrayBaseLength) \
V(MapEnumLength) \
V(FunctionLiteral) \ V(FunctionLiteral) \
V(GetCachedArrayIndex) \ V(GetCachedArrayIndex) \
V(GlobalObject) \ 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> { class LElementsKind: public LTemplateInstruction<1, 1, 0> {
public: public:
explicit LElementsKind(LOperand* value) { explicit LElementsKind(LOperand* value) {

View File

@ -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, void MacroAssembler::DispatchMap(Register obj,
Handle<Map> map, Handle<Map> map,
Handle<Code> success, Handle<Code> success,
@ -4479,52 +4487,38 @@ void MacroAssembler::EnsureNotWhite(
void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
Label next; Label next, start;
Register empty_fixed_array_value = r8; Register empty_fixed_array_value = r8;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Register empty_descriptor_array_value = r9;
LoadRoot(empty_descriptor_array_value,
Heap::kEmptyDescriptorArrayRootIndex);
movq(rcx, rax); 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); bind(&next);
// Check that there are no elements. Register rcx contains the movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
// current JS object we've reached through the prototype chain.
// 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, cmpq(empty_fixed_array_value,
FieldOperand(rcx, JSObject::kElementsOffset)); FieldOperand(rcx, JSObject::kElementsOffset));
j(not_equal, call_runtime); 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)); movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
cmpq(rcx, null_value); cmpq(rcx, null_value);
j(not_equal, &next); j(not_equal, &next);

View File

@ -948,6 +948,15 @@ class MacroAssembler: public Assembler {
void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch); void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);
void LoadInstanceDescriptors(Register map, Register descriptors); void LoadInstanceDescriptors(Register map, Register descriptors);
void EnumLength(Register dst, Register map);
template<typename Field>
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. // Abort execution if argument is not a number. Used in debug code.
void AbortIfNotNumber(Register object); void AbortIfNotNumber(Register object);