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:
parent
b0067e9cd5
commit
78037d0a4e
@ -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);
|
||||||
|
@ -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));
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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) {
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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++) {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user