Revision: 12400
Author:   [email protected]
Date:     Tue Aug 28 07:20:50 2012
Log: 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
http://code.google.com/p/v8/source/detail?r=12400

Modified:
 /branches/bleeding_edge/src/arm/full-codegen-arm.cc
 /branches/bleeding_edge/src/arm/lithium-arm.cc
 /branches/bleeding_edge/src/arm/lithium-arm.h
 /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
 /branches/bleeding_edge/src/arm/macro-assembler-arm.cc
 /branches/bleeding_edge/src/arm/macro-assembler-arm.h
 /branches/bleeding_edge/src/handles.cc
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/hydrogen-instructions.h
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
 /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
 /branches/bleeding_edge/src/ia32/lithium-ia32.cc
 /branches/bleeding_edge/src/ia32/lithium-ia32.h
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/utils.h
 /branches/bleeding_edge/src/x64/full-codegen-x64.cc
 /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
 /branches/bleeding_edge/src/x64/lithium-x64.cc
 /branches/bleeding_edge/src/x64/lithium-x64.h
 /branches/bleeding_edge/src/x64/macro-assembler-x64.cc
 /branches/bleeding_edge/src/x64/macro-assembler-x64.h

=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Aug 27 02:40:26 2012 +++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Aug 28 07:20:50 2012
@@ -1125,26 +1125,34 @@
   // 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);
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.cc      Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/arm/lithium-arm.cc      Tue Aug 28 07:20:50 2012
@@ -1537,6 +1537,12 @@
   LOperand* array = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
 }
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+  LOperand* map = UseRegisterAtStart(instr->value());
+  return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}


 LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.h       Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/arm/lithium-arm.h       Tue Aug 28 07:20:50 2012
@@ -132,6 +132,7 @@
   V(LoadNamedField)                             \
   V(LoadNamedFieldPolymorphic)                  \
   V(LoadNamedGeneric)                           \
+  V(MapEnumLength)                              \
   V(MathFloorOfDiv)                             \
   V(MathMinMax)                                 \
   V(ModI)                                       \
@@ -1002,6 +1003,16 @@
 };


+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) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Aug 28 00:18:06 2012 +++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Aug 28 07:20:50 2012
@@ -1537,6 +1537,13 @@
   Register array = ToRegister(instr->InputAt(0));
   __ ldr(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset));
 }
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+  Register result = ToRegister(instr->result());
+  Register map = ToRegister(instr->InputAt(0));
+  __ EnumLength(result, map);
+}


 void LCodeGen::DoElementsKind(LElementsKind* instr) {
@@ -5558,6 +5565,14 @@
   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 @@
          FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
   __ cmp(result, Operand(0));
   DeoptimizeIf(eq, instr->environment());
+
+  __ bind(&done);
 }


=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Wed Aug 22 07:27:11 2012 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Tue Aug 28 07:20:50 2012
@@ -3715,57 +3715,49 @@
   mov(descriptors, Operand(FACTORY->empty_descriptor_array()));
   bind(&ok);
 }
+
+
+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));
-  cmp(r2, empty_fixed_array_value);
-  b(ne, call_runtime);
+ // Check if the enum length field is properly initialized, indicating that
+  // there is an enum cache.
+  ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));

-  // 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);
+  EnumLength(r3, r1);
+  cmp(r3, Operand(Smi::FromInt(Map::kInvalidEnumCache)));
   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);
+  jmp(&start);
+
+  bind(&next);
+  ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));

   // 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);
+  EnumLength(r3, r1);
+  cmp(r3, Operand(Smi::FromInt(0)));
   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);
+  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);
+
+  ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
+  cmp(r2, null_value);
   b(ne, &next);
 }

=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.h Fri Aug 17 05:59:00 2012 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.h Tue Aug 28 07:20:50 2012
@@ -1272,6 +1272,7 @@
   void LoadInstanceDescriptors(Register map,
                                Register descriptors,
                                Register scratch);
+  void EnumLength(Register dst, Register map);

   // Activation support.
   void EnterFrame(StackFrame::Type type);
=======================================
--- /branches/bleeding_edge/src/handles.cc      Mon Aug 27 06:47:34 2012
+++ /branches/bleeding_edge/src/handles.cc      Tue Aug 28 07:20:50 2012
@@ -704,30 +704,44 @@
   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>(FixedArray::cast(desc->GetEnumCache()),
-                                isolate);
+ Handle<FixedArray> keys(FixedArray::cast(desc->GetEnumCache()), isolate);
+
+      isolate->counters()->enum_cache_hits()->Increment();
+      return keys;
     }
-    isolate->counters()->enum_cache_misses()->Increment();
+
     Handle<Map> map(object->map());
-    int num_enum = object->NumberOfLocalProperties(DONT_ENUM);

- Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum);
-    Handle<FixedArray> indices;
-
-    if (cache_result) {
-      indices = isolate->factory()->NewFixedArray(num_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> indices = isolate->factory()->NewFixedArray(num_enum);

     Handle<DescriptorArray> descs =
Handle<DescriptorArray>(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<FixedArray>();
@@ -742,17 +756,19 @@
         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) {
-      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));
+      object->map()->SetEnumLength(index);
     }
-    ASSERT(storage->length() == index);
     return storage;
   } else {
     int num_enum = object->NumberOfLocalProperties(DONT_ENUM);
=======================================
--- /branches/bleeding_edge/src/heap.cc Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/heap.cc Tue Aug 28 07:20:50 2012
@@ -2090,7 +2090,8 @@
   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.
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Wed Aug 22 08:44:17 2012 +++ /branches/bleeding_edge/src/hydrogen-instructions.h Tue Aug 28 07:20:50 2012
@@ -139,6 +139,7 @@
   V(LoadNamedField)                            \
   V(LoadNamedFieldPolymorphic)                 \
   V(LoadNamedGeneric)                          \
+  V(MapEnumLength)                             \
   V(MathFloorOfDiv)                            \
   V(MathMinMax)                                \
   V(Mod)                                       \
@@ -1923,6 +1924,26 @@
 };


+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) {
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/hydrogen.cc     Tue Aug 28 07:20:50 2012
@@ -4582,15 +4582,14 @@
           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<Object>(Smi::FromInt(0)), Representation::Integer32()));

   Push(map);
   Push(array);
-  Push(array_length);
+  Push(enum_length);
   Push(start_index);

   HInstruction* index_cache = AddInstruction(
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Aug 27 02:40:26 2012 +++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Aug 28 07:20:50 2012
@@ -1091,19 +1091,28 @@


   // 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);
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Tue Aug 28 00:18:06 2012 +++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Tue Aug 28 07:20:50 2012
@@ -1392,6 +1392,13 @@
   Register array = ToRegister(instr->InputAt(0));
   __ mov(result, FieldOperand(array, FixedArrayBase::kLengthOffset));
 }
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+  Register result = ToRegister(instr->result());
+  Register map = ToRegister(instr->InputAt(0));
+  __ EnumLength(result, map);
+}


 void LCodeGen::DoElementsKind(LElementsKind* instr) {
@@ -5454,11 +5461,20 @@
 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());
 }
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.cc Tue Aug 28 00:18:06 2012 +++ /branches/bleeding_edge/src/ia32/lithium-ia32.cc Tue Aug 28 07:20:50 2012
@@ -1585,6 +1585,12 @@
   LOperand* array = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
 }
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+  LOperand* map = UseRegisterAtStart(instr->value());
+  return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}


 LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.h     Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.h     Tue Aug 28 07:20:50 2012
@@ -126,6 +126,7 @@
   V(LoadNamedField)                             \
   V(LoadNamedFieldPolymorphic)                  \
   V(LoadNamedGeneric)                           \
+  V(MapEnumLength)                              \
   V(MathFloorOfDiv)                             \
   V(MathMinMax)                                 \
   V(MathPowHalf)                                \
@@ -1019,6 +1020,16 @@
 };


+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) {
=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Wed Aug 22 08:44:17 2012 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Tue Aug 28 07:20:50 2012
@@ -2895,49 +2895,45 @@

   bind(&done);
 }
+
+
+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);

-  // Load the prototype from the map and loop if non-null.
-  bind(&check_prototype);
+  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);
+
   mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
   cmp(ecx, isolate()->factory()->null_value());
   j(not_equal, &next);
=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Wed Aug 22 08:44:17 2012 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Tue Aug 28 07:20:50 2012
@@ -492,7 +492,15 @@
   }

   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);

   // Abort execution if argument is not a number. Used in debug code.
=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/objects.cc      Tue Aug 28 07:20:50 2012
@@ -4149,16 +4149,13 @@
        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 @@
   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* maybe_result =
       accessor->AddElementsToFixedArray(NULL, NULL, this, other);
   FixedArray* result;
-  if (!maybe_result->To<FixedArray>(&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++) {
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/objects.h       Tue Aug 28 07:20:50 2012
@@ -4663,10 +4663,11 @@
   inline int bit_field3();
   inline void set_bit_field3(int value);

-  class NumberOfOwnDescriptorsBits: public BitField<int,   0, 11> {};
-  class IsShared:                   public BitField<bool, 11,  1> {};
-  class FunctionWithPrototype:      public BitField<bool, 12,  1> {};
-  class DictionaryMap:              public BitField<bool, 13,  1> {};
+  class EnumLengthBits:             public BitField<int,   0, 11> {};
+  class NumberOfOwnDescriptorsBits: public BitField<int,  11, 11> {};
+  class IsShared:                   public BitField<bool, 22,  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
   // for instances created from this function.  If the prototype
@@ -4917,6 +4918,14 @@
   void SetNumberOfOwnDescriptors(int 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* CopyWithPreallocatedFieldDescriptors();
@@ -5057,6 +5066,9 @@

   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;
=======================================
--- /branches/bleeding_edge/src/utils.h Mon Aug  6 07:13:09 2012
+++ /branches/bleeding_edge/src/utils.h Tue Aug 28 07:20:50 2012
@@ -248,6 +248,7 @@
   // 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<T>((1U << size) - 1);
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Aug 27 02:40:26 2012 +++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Aug 28 07:20:50 2012
@@ -1105,22 +1105,32 @@
   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);
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Tue Aug 28 00:18:06 2012 +++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Tue Aug 28 07:20:50 2012
@@ -1283,6 +1283,13 @@
   Register array = ToRegister(instr->InputAt(0));
   __ movq(result, FieldOperand(array, FixedArrayBase::kLengthOffset));
 }
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+  Register result = ToRegister(instr->result());
+  Register map = ToRegister(instr->InputAt(0));
+  __ EnumLength(result, map);
+}


 void LCodeGen::DoElementsKind(LElementsKind* instr) {
@@ -5217,11 +5224,19 @@
 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());
 }
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.cc      Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/x64/lithium-x64.cc      Tue Aug 28 07:20:50 2012
@@ -1526,6 +1526,12 @@
   LOperand* array = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
 }
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+  LOperand* map = UseRegisterAtStart(instr->value());
+  return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}


 LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.h       Tue Aug 28 00:18:06 2012
+++ /branches/bleeding_edge/src/x64/lithium-x64.h       Tue Aug 28 07:20:50 2012
@@ -96,6 +96,7 @@
   V(ElementsKind)                               \
   V(FastLiteral)                                \
   V(FixedArrayBaseLength)                       \
+  V(MapEnumLength)                              \
   V(FunctionLiteral)                            \
   V(GetCachedArrayIndex)                        \
   V(GlobalObject)                               \
@@ -1002,6 +1003,16 @@
 };


+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) {
=======================================
--- /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Wed Aug 22 08:44:17 2012 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Tue Aug 28 07:20:50 2012
@@ -2896,6 +2896,14 @@
   Move(descriptors, isolate()->factory()->empty_descriptor_array());
   bind(&ok);
 }
+
+
+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,
@@ -4479,52 +4487,38 @@


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);
-  bind(&next);

-  // 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.
+ // Check if the enum length field is properly initialized, indicating that
+  // there is an enum cache.
   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);
+  EnumLength(rdx, rbx);
+  Cmp(rdx, Smi::FromInt(Map::kInvalidEnumCache));
   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);
+  jmp(&start);
+
+  bind(&next);
+
+  movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));

   // 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);
+  EnumLength(rdx, rbx);
+  Cmp(rdx, Smi::FromInt(0));
   j(not_equal, call_runtime);

-  // Load the prototype from the map and loop if non-null.
-  bind(&check_prototype);
+  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);
+
   movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
   cmpq(rcx, null_value);
   j(not_equal, &next);
=======================================
--- /branches/bleeding_edge/src/x64/macro-assembler-x64.h Wed Aug 22 08:44:17 2012 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.h Tue Aug 28 07:20:50 2012
@@ -948,6 +948,15 @@
   void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);

   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.
   void AbortIfNotNumber(Register object);

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to