Basic support for tracking smi-only arrays on ia32.
authordanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Sep 2011 11:30:04 +0000 (11:30 +0000)
committerdanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Sep 2011 11:30:04 +0000 (11:30 +0000)
Activated by the flag --smi-only-arrays

Currently not crankshaft support, using flag on non-ia32 platforms will lead to write barrier misses and crashes.

BUG=none
TEST=elements_kind.js

Review URL: http://codereview.chromium.org/7901016

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9392 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

38 files changed:
src/arm/lithium-codegen-arm.cc
src/arm/macro-assembler-arm.cc
src/arm/stub-cache-arm.cc
src/bootstrapper.cc
src/builtins.cc
src/code-stubs.cc
src/elements.cc
src/factory.cc
src/factory.h
src/flag-definitions.h
src/heap.cc
src/heap.h
src/hydrogen-instructions.cc
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/ia32/ic-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/ia32/stub-cache-ia32.cc
src/ic.cc
src/lithium.cc
src/liveobjectlist.cc
src/mips/macro-assembler-mips.cc
src/mips/stub-cache-mips.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/stub-cache.h
src/x64/lithium-codegen-x64.cc
src/x64/macro-assembler-x64.cc
src/x64/stub-cache-x64.cc
test/cctest/test-heap.cc
test/mjsunit/element-kind.js

index 990a093..1e98f67 100644 (file)
@@ -2615,6 +2615,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
       case EXTERNAL_DOUBLE_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case FAST_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case DICTIONARY_ELEMENTS:
       case NON_STRICT_ARGUMENTS_ELEMENTS:
         UNREACHABLE();
@@ -3457,6 +3458,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
       case EXTERNAL_DOUBLE_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case FAST_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case DICTIONARY_ELEMENTS:
       case NON_STRICT_ARGUMENTS_ELEMENTS:
         UNREACHABLE();
index 7f5a81c..a77fd92 100644 (file)
@@ -1859,7 +1859,8 @@ void MacroAssembler::CompareRoot(Register obj,
 void MacroAssembler::CheckFastElements(Register map,
                                        Register scratch,
                                        Label* fail) {
-  STATIC_ASSERT(FAST_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_ELEMENTS == 1);
   ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset));
   cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue));
   b(hi, fail);
index 2f004d8..b005f59 100644 (file)
@@ -3493,6 +3493,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) {
     case EXTERNAL_FLOAT_ELEMENTS:
     case EXTERNAL_DOUBLE_ELEMENTS:
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3579,6 +3580,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray(
       }
       break;
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3919,6 +3921,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
       }
       break;
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3982,6 +3985,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
           case EXTERNAL_FLOAT_ELEMENTS:
           case EXTERNAL_DOUBLE_ELEMENTS:
           case FAST_ELEMENTS:
+          case FAST_SMI_ONLY_ELEMENTS:
           case FAST_DOUBLE_ELEMENTS:
           case DICTIONARY_ELEMENTS:
           case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -4121,6 +4125,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
           case EXTERNAL_FLOAT_ELEMENTS:
           case EXTERNAL_DOUBLE_ELEMENTS:
           case FAST_ELEMENTS:
+          case FAST_SMI_ONLY_ELEMENTS:
           case FAST_DOUBLE_ELEMENTS:
           case DICTIONARY_ELEMENTS:
           case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -4273,8 +4278,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
 }
 
 
-void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
-                                                      bool is_js_array) {
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+    MacroAssembler* masm,
+    bool is_js_array,
+    ElementsKind elements_kind) {
   // ----------- S t a t e -------------
   //  -- r0    : value
   //  -- r1    : key
@@ -4324,6 +4331,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
          Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
   __ str(value_reg, MemOperand(scratch));
   __ mov(receiver_reg, value_reg);
+  ASSERT(elements_kind == FAST_ELEMENTS);
   __ RecordWrite(elements_reg,  // Object.
                  scratch,       // Address.
                  receiver_reg,  // Value.
index 6fde0b6..25467d1 100644 (file)
@@ -1076,6 +1076,11 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
     elements->set(0, *array);
     array = factory->NewFixedArray(0);
     elements->set(1, *array);
+    Handle<Map> non_strict_arguments_elements_map =
+        factory->GetElementsTransitionMap(result,
+                                          NON_STRICT_ARGUMENTS_ELEMENTS);
+    result->set_map(*non_strict_arguments_elements_map);
+    ASSERT(result->HasNonStrictArgumentsElements());
     result->set_elements(*elements);
     global_context()->set_aliased_arguments_boilerplate(*result);
   }
@@ -1557,6 +1562,18 @@ bool Genesis::InstallNatives() {
         isolate()->builtins()->builtin(Builtins::kArrayConstructCode));
     array_function->shared()->DontAdaptArguments();
 
+    // InternalArrays should not use Smi-Only array optimizations. There are too
+    // many places in the C++ runtime code (e.g. RegEx) that assume that
+    // elements in InternalArrays can be set to non-Smi values without going
+    // through a common bottleneck that would make the SMI_ONLY -> FAST_ELEMENT
+    // transition easy to trap. Moreover, they rarely are smi-only.
+    MaybeObject* maybe_map =
+        array_function->initial_map()->CopyDropTransitions();
+    Map* new_map;
+    if (!maybe_map->To<Map>(&new_map)) return maybe_map;
+    new_map->set_elements_kind(FAST_ELEMENTS);
+    array_function->set_initial_map(new_map);
+
     // Make "length" magic on instances.
     Handle<DescriptorArray> array_descriptors =
         factory()->CopyAppendForeignDescriptor(
index 4af5b3a..591904e 100644 (file)
@@ -203,7 +203,7 @@ BUILTIN(ArrayCodeGeneric) {
   }
 
   // 'array' now contains the JSArray we should initialize.
-  ASSERT(array->HasFastElements());
+  ASSERT(array->HasFastTypeElements());
 
   // Optimize the case where there is one argument and the argument is a
   // small smi.
@@ -216,7 +216,8 @@ BUILTIN(ArrayCodeGeneric) {
         { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len);
           if (!maybe_obj->ToObject(&obj)) return maybe_obj;
         }
-        array->SetContent(FixedArray::cast(obj));
+        MaybeObject* maybe_obj = array->SetContent(FixedArray::cast(obj));
+        if (maybe_obj->IsFailure()) return maybe_obj;
         return array;
       }
     }
@@ -240,6 +241,13 @@ BUILTIN(ArrayCodeGeneric) {
     if (!maybe_obj->ToObject(&obj)) return maybe_obj;
   }
 
+  // Set length and elements on the array.
+  if (FLAG_smi_only_arrays) {
+    MaybeObject* maybe_object =
+        array->EnsureCanContainElements(FixedArray::cast(obj));
+    if (maybe_object->IsFailure()) return maybe_object;
+  }
+
   AssertNoAllocation no_gc;
   FixedArray* elms = FixedArray::cast(obj);
   WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
@@ -248,7 +256,6 @@ BUILTIN(ArrayCodeGeneric) {
     elms->set(index, args[index+1], mode);
   }
 
-  // Set length and elements on the array.
   array->set_elements(FixedArray::cast(obj));
   array->set_length(len);
 
@@ -486,9 +493,11 @@ BUILTIN(ArrayPush) {
     FillWithHoles(heap, new_elms, new_length, capacity);
 
     elms = new_elms;
-    array->set_elements(elms);
   }
 
+  MaybeObject* maybe = array->EnsureCanContainElements(&args, 1, to_add);
+  if (maybe->IsFailure()) return maybe;
+
   // Add the provided values.
   AssertNoAllocation no_gc;
   WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
@@ -496,6 +505,10 @@ BUILTIN(ArrayPush) {
     elms->set(index + len, args[index + 1], mode);
   }
 
+  if (elms != array->elements()) {
+    array->set_elements(elms);
+  }
+
   // Set the length.
   array->set_length(Smi::FromInt(new_length));
   return Smi::FromInt(new_length);
@@ -550,7 +563,7 @@ BUILTIN(ArrayShift) {
   }
   FixedArray* elms = FixedArray::cast(elms_obj);
   JSArray* array = JSArray::cast(receiver);
-  ASSERT(array->HasFastElements());
+  ASSERT(array->HasFastTypeElements());
 
   int len = Smi::cast(array->length())->value();
   if (len == 0) return heap->undefined_value();
@@ -592,7 +605,7 @@ BUILTIN(ArrayUnshift) {
   }
   FixedArray* elms = FixedArray::cast(elms_obj);
   JSArray* array = JSArray::cast(receiver);
-  ASSERT(array->HasFastElements());
+  ASSERT(array->HasFastTypeElements());
 
   int len = Smi::cast(array->length())->value();
   int to_add = args.length() - 1;
@@ -601,6 +614,12 @@ BUILTIN(ArrayUnshift) {
   // we should never hit this case.
   ASSERT(to_add <= (Smi::kMaxValue - len));
 
+  if (FLAG_smi_only_arrays) {
+    MaybeObject* maybe_object =
+        array->EnsureCanContainElements(&args, 1, to_add);
+    if (maybe_object->IsFailure()) return maybe_object;
+  }
+
   if (new_length > elms->length()) {
     // New backing storage is needed.
     int capacity = new_length + (new_length >> 1) + 16;
@@ -609,13 +628,11 @@ BUILTIN(ArrayUnshift) {
       if (!maybe_obj->ToObject(&obj)) return maybe_obj;
     }
     FixedArray* new_elms = FixedArray::cast(obj);
-
     AssertNoAllocation no_gc;
     if (len > 0) {
       CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len);
     }
     FillWithHoles(heap, new_elms, new_length, capacity);
-
     elms = new_elms;
     array->set_elements(elms);
   } else {
@@ -730,6 +747,12 @@ BUILTIN(ArraySlice) {
   }
   FixedArray* result_elms = FixedArray::cast(result);
 
+  if (FLAG_smi_only_arrays) {
+    MaybeObject* maybe_object =
+        result_array->EnsureCanContainElements(result_elms);
+    if (maybe_object->IsFailure()) return maybe_object;
+  }
+
   AssertNoAllocation no_gc;
   CopyElements(heap, &no_gc, result_elms, 0, elms, k, result_len);
 
@@ -757,7 +780,7 @@ BUILTIN(ArraySplice) {
   }
   FixedArray* elms = FixedArray::cast(elms_obj);
   JSArray* array = JSArray::cast(receiver);
-  ASSERT(array->HasFastElements());
+  ASSERT(array->HasFastTypeElements());
 
   int len = Smi::cast(array->length())->value();
 
@@ -835,8 +858,14 @@ BUILTIN(ArraySplice) {
 
   int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
 
+  if (FLAG_smi_only_arrays) {
+    MaybeObject* maybe = array->EnsureCanContainElements(&args, 3, item_count);
+    if (maybe->IsFailure()) return maybe;
+  }
+
   int new_length = len - actual_delete_count + item_count;
 
+  bool elms_changed = false;
   if (item_count < actual_delete_count) {
     // Shrink the array.
     const bool trim_array = !heap->lo_space()->Contains(elms) &&
@@ -851,7 +880,8 @@ BUILTIN(ArraySplice) {
       }
 
       elms = LeftTrimFixedArray(heap, elms, delta);
-      array->set_elements(elms);
+
+      elms_changed = true;
     } else {
       AssertNoAllocation no_gc;
       MoveElements(heap, &no_gc,
@@ -891,7 +921,7 @@ BUILTIN(ArraySplice) {
       FillWithHoles(heap, new_elms, new_length, capacity);
 
       elms = new_elms;
-      array->set_elements(elms);
+      elms_changed = true;
     } else {
       AssertNoAllocation no_gc;
       MoveElements(heap, &no_gc,
@@ -907,6 +937,10 @@ BUILTIN(ArraySplice) {
     elms->set(k, args[3 + k - actual_start], mode);
   }
 
+  if (elms_changed) {
+    array->set_elements(elms);
+  }
+
   // Set the length.
   array->set_length(Smi::FromInt(new_length));
 
@@ -965,6 +999,19 @@ BUILTIN(ArrayConcat) {
   }
   FixedArray* result_elms = FixedArray::cast(result);
 
+  if (FLAG_smi_only_arrays) {
+    for (int i = 0; i < n_arguments; i++) {
+      JSArray* array = JSArray::cast(args[i]);
+      int len = Smi::cast(array->length())->value();
+      if (len > 0) {
+        FixedArray* elms = FixedArray::cast(array->elements());
+        MaybeObject* maybe_object =
+            result_array->EnsureCanContainElements(elms);
+        if (maybe_object->IsFailure()) return maybe_object;
+      }
+    }
+  }
+
   // Copy data.
   AssertNoAllocation no_gc;
   int start_pos = 0;
index f309a82..6670796 100644 (file)
@@ -250,6 +250,7 @@ void InstanceofStub::PrintName(StringStream* stream) {
 void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
   switch (elements_kind_) {
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
       KeyedLoadStubCompiler::GenerateLoadFastElement(masm);
       break;
     case FAST_DOUBLE_ELEMENTS:
@@ -279,7 +280,11 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
 void KeyedStoreElementStub::Generate(MacroAssembler* masm) {
   switch (elements_kind_) {
     case FAST_ELEMENTS:
-      KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_);
+    case FAST_SMI_ONLY_ELEMENTS: {
+      KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
+                                                       is_js_array_,
+                                                       elements_kind_);
+    }
       break;
     case FAST_DOUBLE_ELEMENTS:
       KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
index e4ecfe8..5e7a84e 100644 (file)
@@ -227,7 +227,9 @@ class FastElementsAccessor
  public:
   static MaybeObject* DeleteCommon(JSObject* obj,
                                    uint32_t key) {
-    ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements());
+    ASSERT(obj->HasFastElements() ||
+           obj->HasFastSmiOnlyElements() ||
+           obj->HasFastArgumentsElements());
     Heap* heap = obj->GetHeap();
     FixedArray* backing_store = FixedArray::cast(obj->elements());
     if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
@@ -596,6 +598,9 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) {
 
 void ElementsAccessor::InitializeOncePerProcess() {
   static struct ConcreteElementsAccessors {
+    // Use the fast element handler for smi-only arrays. The implementation is
+    // currently identical.
+    FastElementsAccessor fast_smi_elements_handler;
     FastElementsAccessor fast_elements_handler;
     FastDoubleElementsAccessor fast_double_elements_handler;
     DictionaryElementsAccessor dictionary_elements_handler;
@@ -612,6 +617,7 @@ void ElementsAccessor::InitializeOncePerProcess() {
   } element_accessors;
 
   static ElementsAccessor* accessor_array[] = {
+    &element_accessors.fast_smi_elements_handler,
     &element_accessors.fast_elements_handler,
     &element_accessors.fast_double_elements_handler,
     &element_accessors.dictionary_elements_handler,
@@ -627,6 +633,9 @@ void ElementsAccessor::InitializeOncePerProcess() {
     &element_accessors.pixel_elements_handler
   };
 
+  STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
+                kElementsKindCount);
+
   elements_accessors_ = accessor_array;
 }
 
index 9ed3c44..a40dbd7 100644 (file)
@@ -404,10 +404,12 @@ Handle<JSGlobalPropertyCell> Factory::NewJSGlobalPropertyCell(
 }
 
 
-Handle<Map> Factory::NewMap(InstanceType type, int instance_size) {
+Handle<Map> Factory::NewMap(InstanceType type,
+                            int instance_size,
+                            ElementsKind elements_kind) {
   CALL_HEAP_FUNCTION(
       isolate(),
-      isolate()->heap()->AllocateMap(type, instance_size),
+      isolate()->heap()->AllocateMap(type, instance_size, elements_kind),
       Map);
 }
 
@@ -710,7 +712,12 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name,
   if (force_initial_map ||
       type != JS_OBJECT_TYPE ||
       instance_size != JSObject::kHeaderSize) {
-    Handle<Map> initial_map = NewMap(type, instance_size);
+    ElementsKind default_elements_kind = FLAG_smi_only_arrays
+        ? FAST_SMI_ONLY_ELEMENTS
+        : FAST_ELEMENTS;
+    Handle<Map> initial_map = NewMap(type,
+                                     instance_size,
+                                     default_elements_kind);
     function->set_initial_map(*initial_map);
     initial_map->set_constructor(*function);
   }
@@ -896,11 +903,26 @@ Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements,
   Handle<JSArray> result =
       Handle<JSArray>::cast(NewJSObject(isolate()->array_function(),
                                         pretenure));
-  result->SetContent(*elements);
+  SetContent(result, elements);
   return result;
 }
 
 
+void Factory::SetContent(Handle<JSArray> array,
+                         Handle<FixedArray> elements) {
+  CALL_HEAP_FUNCTION_VOID(
+      isolate(),
+      array->SetContent(*elements));
+}
+
+
+void Factory::EnsureCanContainNonSmiElements(Handle<JSArray> array) {
+  CALL_HEAP_FUNCTION_VOID(
+      isolate(),
+      array->EnsureCanContainNonSmiElements());
+}
+
+
 Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
                                     Handle<Object> prototype) {
   CALL_HEAP_FUNCTION(
index 422f7e7..819c1a6 100644 (file)
@@ -203,7 +203,9 @@ class Factory {
   Handle<JSGlobalPropertyCell> NewJSGlobalPropertyCell(
       Handle<Object> value);
 
-  Handle<Map> NewMap(InstanceType type, int instance_size);
+  Handle<Map> NewMap(InstanceType type,
+                     int instance_size,
+                     ElementsKind elements_kind = FAST_ELEMENTS);
 
   Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
 
@@ -253,6 +255,10 @@ class Factory {
       Handle<FixedArray> elements,
       PretenureFlag pretenure = NOT_TENURED);
 
+  void SetContent(Handle<JSArray> array, Handle<FixedArray> elements);
+
+  void EnsureCanContainNonSmiElements(Handle<JSArray> array);
+
   Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
 
   // Change the type of the argument into a JS object/function and reinitialize.
index 161dba4..358d67e 100644 (file)
@@ -104,6 +104,7 @@ DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping")
 
 // Flags for experimental implementation features.
 DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
+DEFINE_bool(smi_only_arrays, false, "tracks arrays with only smi values")
 DEFINE_bool(string_slices, false, "use string slices")
 
 // Flags for Crankshaft.
index 45aa577..9000c2d 100644 (file)
@@ -1700,7 +1700,9 @@ MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type,
 }
 
 
-MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
+MaybeObject* Heap::AllocateMap(InstanceType instance_type,
+                               int instance_size,
+                               ElementsKind elements_kind) {
   Object* result;
   { MaybeObject* maybe_result = AllocateRawMap();
     if (!maybe_result->ToObject(&result)) return maybe_result;
@@ -1722,7 +1724,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
   map->set_unused_property_fields(0);
   map->set_bit_field(0);
   map->set_bit_field2(1 << Map::kIsExtensible);
-  map->set_elements_kind(FAST_ELEMENTS);
+  map->set_elements_kind(elements_kind);
 
   // If the map object is aligned fill the padding area with Smi 0 objects.
   if (Map::kPadStart < Map::kSize) {
@@ -2112,7 +2114,13 @@ bool Heap::CreateApiObjects() {
   { MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     if (!maybe_obj->ToObject(&obj)) return false;
   }
-  set_neander_map(Map::cast(obj));
+  // Don't use Smi-only elements optimizations for objects with the neander
+  // map. There are too many cases where element values are set directly with a
+  // bottleneck to trap the Smi-only -> fast elements transition, and there
+  // appears to be no benefit for optimize this case.
+  Map* new_neander_map = Map::cast(obj);
+  new_neander_map->set_elements_kind(FAST_ELEMENTS);
+  set_neander_map(new_neander_map);
 
   { MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map());
     if (!maybe_obj->ToObject(&obj)) return false;
@@ -3503,7 +3511,8 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {
   InitializeJSObjectFromMap(JSObject::cast(obj),
                             FixedArray::cast(properties),
                             map);
-  ASSERT(JSObject::cast(obj)->HasFastElements());
+  ASSERT(JSObject::cast(obj)->HasFastSmiOnlyElements() ||
+         JSObject::cast(obj)->HasFastElements());
   return obj;
 }
 
@@ -3685,6 +3694,7 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) {
               object_size);
   }
 
+  ASSERT(JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind());
   FixedArrayBase* elements = FixedArrayBase::cast(source->elements());
   FixedArray* properties = FixedArray::cast(source->properties());
   // Update elements if necessary.
index d89506e..7f05b4e 100644 (file)
@@ -523,8 +523,10 @@ class Heap {
   // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
   // failed.
   // Please note this function does not perform a garbage collection.
-  MUST_USE_RESULT MaybeObject* AllocateMap(InstanceType instance_type,
-                                           int instance_size);
+  MUST_USE_RESULT MaybeObject* AllocateMap(
+      InstanceType instance_type,
+      int instance_size,
+      ElementsKind elements_kind = FAST_ELEMENTS);
 
   // Allocates a partial map for bootstrapping.
   MUST_USE_RESULT MaybeObject* AllocatePartialMap(InstanceType instance_type,
index 5630ce3..658f5e1 100644 (file)
@@ -1488,6 +1488,7 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo(
       stream->Add("pixel");
       break;
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -1582,6 +1583,7 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo(
     case EXTERNAL_PIXEL_ELEMENTS:
       stream->Add("pixel");
       break;
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
index 4d6b53b..f6588bc 100644 (file)
@@ -3336,7 +3336,12 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
     HValue* key = AddInstruction(
         new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
                               Representation::Integer32()));
-    AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
+    if (FLAG_smi_only_arrays) {
+      AddInstruction(BuildStoreKeyedGeneric(literal, key, value));
+    } else {
+      AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
+    }
+
     AddSimulate(expr->GetIdForElement(i));
   }
   return ast_context()->ReturnValue(Pop());
@@ -3947,6 +3952,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
       case EXTERNAL_FLOAT_ELEMENTS:
       case EXTERNAL_DOUBLE_ELEMENTS:
         break;
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case DICTIONARY_ELEMENTS:
@@ -4058,14 +4064,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
   HLoadExternalArrayPointer* external_elements = NULL;
   HInstruction* checked_key = NULL;
 
-  // FAST_ELEMENTS is assumed to be the first case.
-  STATIC_ASSERT(FAST_ELEMENTS == 0);
+  // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS,
+  // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external
+  // arrays.
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
+  STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
+  STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
+  STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
 
-  for (ElementsKind elements_kind = FAST_ELEMENTS;
+  for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
        elements_kind <= LAST_ELEMENTS_KIND;
        elements_kind = ElementsKind(elements_kind + 1)) {
-    // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we
-    // need to add some code that's executed for all external array cases.
+    // After having handled FAST_ELEMENTS, FAST_SMI_ELEMENTS,
+    // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code
+    // that's executed for all external array cases.
     STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
                   LAST_ELEMENTS_KIND);
     if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
@@ -4087,11 +4099,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
 
       set_current_block(if_true);
       HInstruction* access;
-      if (elements_kind == FAST_ELEMENTS ||
+      if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
+          elements_kind == FAST_ELEMENTS ||
           elements_kind == FAST_DOUBLE_ELEMENTS) {
         bool fast_double_elements =
             elements_kind == FAST_DOUBLE_ELEMENTS;
-        if (is_store && elements_kind == FAST_ELEMENTS) {
+        if (is_store && !fast_double_elements) {
           AddInstruction(new(zone()) HCheckMap(
               elements, isolate()->factory()->fixed_array_map(),
               elements_kind_branch));
@@ -4105,29 +4118,40 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
         current_block()->Finish(typecheck);
 
         set_current_block(if_jsarray);
-        HInstruction* length = new(zone()) HJSArrayLength(object, typecheck);
-        AddInstruction(length);
-        checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
-        if (is_store) {
-          if (fast_double_elements) {
-            access = AddInstruction(
-                new(zone()) HStoreKeyedFastDoubleElement(elements,
-                                                         checked_key,
-                                                         val));
-          } else {
-            access = AddInstruction(
-                new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
-          }
+        HInstruction* length;
+        if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
+          // For now, fall back to the generic stub for
+          // FAST_SMI_ONLY_ELEMENTS
+          access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
         } else {
-          if (fast_double_elements) {
-            access = AddInstruction(
-                new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
+          length = new(zone()) HJSArrayLength(object, typecheck);
+          AddInstruction(length);
+          checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
+          if (is_store) {
+            if (fast_double_elements) {
+              access = AddInstruction(
+                  new(zone()) HStoreKeyedFastDoubleElement(elements,
+                                                           checked_key,
+                                                           val));
+            } else {
+              access = AddInstruction(
+                  new(zone()) HStoreKeyedFastElement(elements,
+                                                     checked_key,
+                                                     val));
+            }
           } else {
-            access = AddInstruction(
-                new(zone()) HLoadKeyedFastElement(elements, checked_key));
+            if (fast_double_elements) {
+              access = AddInstruction(
+                  new(zone()) HLoadKeyedFastDoubleElement(elements,
+                                                          checked_key));
+            } else {
+              access = AddInstruction(
+                  new(zone()) HLoadKeyedFastElement(elements, checked_key));
+            }
+            Push(access);
           }
-          Push(access);
         }
+
         *has_side_effects |= access->HasSideEffects();
         if (position != -1) {
           access->set_position(position);
index 738d23f..0c7dd2d 100644 (file)
@@ -1492,8 +1492,20 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
     int offset = FixedArray::kHeaderSize + (i * kPointerSize);
     __ mov(FieldOperand(ebx, offset), result_register());
 
+    Label no_map_change;
+    __ JumpIfSmi(result_register(), &no_map_change);
     // Update the write barrier for the array store.
-    __ RecordWriteField(ebx, offset, result_register(), ecx, kDontSaveFPRegs);
+    __ RecordWriteField(ebx, offset, result_register(), ecx,
+                        kDontSaveFPRegs,
+                        EMIT_REMEMBERED_SET,
+                        OMIT_SMI_CHECK);
+    if (FLAG_smi_only_arrays) {
+      __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
+      __ CheckFastSmiOnlyElements(edi, &no_map_change, Label::kNear);
+      __ push(Operand(esp, 0));
+      __ CallRuntime(Runtime::kNonSmiElementStored, 1);
+    }
+    __ bind(&no_map_change);
 
     PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
   }
index e81357c..c2181e7 100644 (file)
@@ -810,6 +810,19 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
   // ecx: key (a smi)
   // edx: receiver
   // edi: FixedArray receiver->elements
+
+  if (FLAG_smi_only_arrays) {
+    Label not_smi_only;
+    // Make sure the elements are smi-only.
+    __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
+    __ CheckFastSmiOnlyElements(ebx, &not_smi_only, Label::kNear);
+    // Non-smis need to call into the runtime if the array is smi only.
+    __ JumpIfNotSmi(eax, &slow);
+    __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
+    __ ret(0);
+    __ bind(&not_smi_only);
+  }
+
   __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
 
   // Update write barrier for the elements array address.
index 46d8050..fb67429 100644 (file)
@@ -2400,6 +2400,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
         break;
       case EXTERNAL_FLOAT_ELEMENTS:
       case EXTERNAL_DOUBLE_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case DICTIONARY_ELEMENTS:
@@ -3171,6 +3172,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
         break;
       case EXTERNAL_FLOAT_ELEMENTS:
       case EXTERNAL_DOUBLE_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case DICTIONARY_ELEMENTS:
index 519210c..405ae51 100644 (file)
@@ -374,13 +374,38 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
 void MacroAssembler::CheckFastElements(Register map,
                                        Label* fail,
                                        Label::Distance distance) {
-  STATIC_ASSERT(FAST_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_ELEMENTS == 1);
   cmpb(FieldOperand(map, Map::kBitField2Offset),
        Map::kMaximumBitField2FastElementValue);
   j(above, fail, distance);
 }
 
 
+void MacroAssembler::CheckFastObjectElements(Register map,
+                                             Label* fail,
+                                             Label::Distance distance) {
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_ELEMENTS == 1);
+  cmpb(FieldOperand(map, Map::kBitField2Offset),
+       Map::kMaximumBitField2FastSmiOnlyElementValue);
+  j(below_equal, fail, distance);
+  cmpb(FieldOperand(map, Map::kBitField2Offset),
+       Map::kMaximumBitField2FastElementValue);
+  j(above, fail, distance);
+}
+
+
+void MacroAssembler::CheckFastSmiOnlyElements(Register map,
+                                              Label* fail,
+                                              Label::Distance distance) {
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  cmpb(FieldOperand(map, Map::kBitField2Offset),
+       Map::kMaximumBitField2FastSmiOnlyElementValue);
+  j(above, fail, distance);
+}
+
+
 void MacroAssembler::CheckMap(Register obj,
                               Handle<Map> map,
                               Label* fail,
index a59dee8..d097da8 100644 (file)
@@ -312,6 +312,18 @@ class MacroAssembler: public Assembler {
                          Label* fail,
                          Label::Distance distance = Label::kFar);
 
+  // Check if a map for a JSObject indicates that the object can have both smi
+  // and HeapObject elements.  Jump to the specified label if it does not.
+  void CheckFastObjectElements(Register map,
+                               Label* fail,
+                               Label::Distance distance = Label::kFar);
+
+  // Check if a map for a JSObject indicates that the object has fast smi only
+  // elements.  Jump to the specified label if it does not.
+  void CheckFastSmiOnlyElements(Register map,
+                                Label* fail,
+                                Label::Distance distance = Label::kFar);
+
   // Check if the map of an object is equal to a specified map and branch to
   // label if not. Skip the smi check if not required (object is known to be a
   // heap object)
index c3cec4b..b4eff5a 100644 (file)
@@ -1454,7 +1454,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
     __ j(not_equal, &call_builtin);
 
     if (argc == 1) {  // Otherwise fall through to call builtin.
-      Label exit, attempt_to_grow_elements, with_write_barrier;
+      Label attempt_to_grow_elements, with_write_barrier;
 
       // Get the array's length into eax and calculate new length.
       __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
@@ -1469,6 +1469,10 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
       __ cmp(eax, Operand(ecx));
       __ j(greater, &attempt_to_grow_elements);
 
+      // Check if value is a smi.
+      __ mov(ecx, Operand(esp, argc * kPointerSize));
+      __ JumpIfNotSmi(ecx, &with_write_barrier);
+
       // Save new length.
       __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
 
@@ -1476,17 +1480,26 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
       __ lea(edx, FieldOperand(ebx,
                                eax, times_half_pointer_size,
                                FixedArray::kHeaderSize - argc * kPointerSize));
-      __ mov(ecx, Operand(esp, argc * kPointerSize));
       __ mov(Operand(edx, 0), ecx);
 
-      // Check if value is a smi.
-      __ JumpIfNotSmi(ecx, &with_write_barrier);
-
-      __ bind(&exit);
       __ ret((argc + 1) * kPointerSize);
 
       __ bind(&with_write_barrier);
 
+      if (FLAG_smi_only_arrays) {
+        __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
+        __ CheckFastObjectElements(edi, &call_builtin);
+      }
+
+      // Save new length.
+      __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
+
+      // Push the element.
+      __ lea(edx, FieldOperand(ebx,
+                               eax, times_half_pointer_size,
+                               FixedArray::kHeaderSize - argc * kPointerSize));
+      __ mov(Operand(edx, 0), ecx);
+
       __ RecordWrite(
           ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
 
@@ -1497,6 +1510,17 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
         __ jmp(&call_builtin);
       }
 
+      __ mov(edi, Operand(esp, argc * kPointerSize));
+      if (FLAG_smi_only_arrays) {
+        // Growing elements that are SMI-only requires special handling in case
+        // the new element is non-Smi. For now, delegate to the builtin.
+        Label no_fast_elements_check;
+        __ JumpIfSmi(edi, &no_fast_elements_check);
+        __ mov(esi, FieldOperand(edx, HeapObject::kMapOffset));
+        __ CheckFastObjectElements(esi, &call_builtin, Label::kFar);
+        __ bind(&no_fast_elements_check);
+      }
+
       // We could be lucky and the elements array could be at the top of
       // new-space.  In this case we can just grow it in place by moving the
       // allocation pointer up.
@@ -1522,10 +1546,9 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
 
       // We fit and could grow elements.
       __ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
-      __ mov(ecx, Operand(esp, argc * kPointerSize));
 
       // Push the argument...
-      __ mov(Operand(edx, 0), ecx);
+      __ mov(Operand(edx, 0), edi);
       // ... and fill the rest with holes.
       for (int i = 1; i < kAllocationDelta; i++) {
         __ mov(Operand(edx, i * kPointerSize),
@@ -1537,7 +1560,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
       // tell the incremental marker to rescan the object that we just grew.  We
       // don't need to worry about the holes because they are in old space and
       // already marked black.
-      __ RecordWrite(ebx, edx, ecx, kDontSaveFPRegs, OMIT_REMEMBERED_SET);
+      __ RecordWrite(ebx, edx, edi, kDontSaveFPRegs, OMIT_REMEMBERED_SET);
 
       // Restore receiver to edx as finish sequence assumes it's here.
       __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -3877,8 +3900,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
 }
 
 
-void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
-                                                      bool is_js_array) {
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+    MacroAssembler* masm,
+    bool is_js_array,
+    ElementsKind elements_kind) {
   // ----------- S t a t e -------------
   //  -- eax    : value
   //  -- ecx    : key
@@ -3909,12 +3934,28 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
     __ j(above_equal, &miss_force_generic);
   }
 
-  // Do the store and update the write barrier.
-  __ lea(ecx, FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize));
-  __ mov(Operand(ecx, 0), eax);
-  // Make sure to preserve the value in register eax.
-  __ mov(edx, Operand(eax));
-  __ RecordWrite(edi, ecx, edx, kDontSaveFPRegs);
+  if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
+    __ JumpIfNotSmi(eax, &miss_force_generic);
+    // ecx is a smi, don't use times_half_pointer_size istead of
+    // times_pointer_size
+    __ mov(FieldOperand(edi,
+                        ecx,
+                        times_half_pointer_size,
+                        FixedArray::kHeaderSize), eax);
+  } else {
+    ASSERT(elements_kind == FAST_ELEMENTS);
+    // Do the store and update the write barrier.
+    // ecx is a smi, don't use times_half_pointer_size istead of
+    // times_pointer_size
+    __ lea(ecx, FieldOperand(edi,
+                             ecx,
+                             times_half_pointer_size,
+                             FixedArray::kHeaderSize));
+    __ mov(Operand(ecx, 0), eax);
+    // Make sure to preserve the value in register eax.
+    __ mov(edx, Operand(eax));
+    __ RecordWrite(edi, ecx, edx, kDontSaveFPRegs);
+  }
 
   // Done.
   __ ret(0);
index 84a62cd..1457d75 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1673,6 +1673,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
   } else {
     ASSERT(receiver_map->has_dictionary_elements() ||
            receiver_map->has_fast_elements() ||
+           receiver_map->has_fast_smi_only_elements() ||
            receiver_map->has_fast_double_elements() ||
            receiver_map->has_external_array_elements());
     bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
index d3d2238..31b1698 100644 (file)
@@ -203,6 +203,7 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) {
     case EXTERNAL_DOUBLE_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
       return 3;
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
index 54a2331..d62c4d1 100644 (file)
@@ -1336,7 +1336,9 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
   // Allocate the JSArray of the elements.
   Handle<JSObject> elements = factory->NewJSObject(isolate->array_function());
   if (elements->IsFailure()) return Object::cast(*elements);
-  Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
+
+  maybe_result = Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
+  if (maybe_result->IsFailure()) return maybe_result;
 
   // Set body.elements.
   Handle<String> elements_sym = factory->LookupAsciiSymbol("elements");
@@ -1462,7 +1464,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
   Handle<JSObject> summary_obj =
     factory->NewJSObject(isolate->array_function());
   if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
-  Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
+
+  maybe_result = Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
+  if (maybe_result->IsFailure()) return maybe_result;
 
   // Create the body object.
   Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
@@ -1589,7 +1593,9 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
 
   // Return the result as a JS array.
   Handle<JSObject> lols = factory->NewJSObject(isolate->array_function());
-  Handle<JSArray>::cast(lols)->SetContent(*list);
+
+  maybe_result = Handle<JSArray>::cast(lols)->SetContent(*list);
+  if (maybe_result->IsFailure()) return maybe_result;
 
   Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
   if (result->IsFailure()) return Object::cast(*result);
index b3ff82a..fd89d57 100644 (file)
@@ -3163,7 +3163,8 @@ void MacroAssembler::CopyBytes(Register src,
 void MacroAssembler::CheckFastElements(Register map,
                                        Register scratch,
                                        Label* fail) {
-  STATIC_ASSERT(FAST_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_ELEMENTS == 1);
   lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
   Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue));
 }
index 3dd7827..c17a8e8 100644 (file)
@@ -3471,6 +3471,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) {
 
     case EXTERNAL_FLOAT_ELEMENTS:
     case EXTERNAL_DOUBLE_ELEMENTS:
+    case FAST_SMI_ELEMENTS:
     case FAST_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
index bb384cb..f62c828 100644 (file)
@@ -268,7 +268,7 @@ void JSObject::JSObjectVerify() {
              (map()->inobject_properties() + properties()->length() -
               map()->NextFreePropertyIndex()));
   }
-  ASSERT_EQ(map()->has_fast_elements(),
+  ASSERT_EQ((map()->has_fast_elements() || map()->has_fast_smi_only_elements()),
             (elements()->map() == GetHeap()->fixed_array_map() ||
              elements()->map() == GetHeap()->fixed_cow_array_map()));
   ASSERT(map()->has_fast_elements() == HasFastElements());
@@ -330,7 +330,8 @@ void FixedDoubleArray::FixedDoubleArrayVerify() {
       double value = get_scalar(i);
       ASSERT(!isnan(value) ||
              (BitCast<uint64_t>(value) ==
-              BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())));
+              BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())) ||
+             ((BitCast<uint64_t>(value) & Double::kSignMask) != 0));
     }
   }
 }
index 5fadaa9..3b3f625 100644 (file)
@@ -1300,14 +1300,82 @@ FixedArrayBase* JSObject::elements() {
   return static_cast<FixedArrayBase*>(array);
 }
 
+void JSObject::ValidateSmiOnlyElements() {
+#if DEBUG
+  if (FLAG_smi_only_arrays &&
+      map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) {
+    Heap* heap = GetHeap();
+    // Don't use elements, since integrity checks will fail if there
+    // are filler pointers in the array.
+    FixedArray* fixed_array =
+        reinterpret_cast<FixedArray*>(READ_FIELD(this, kElementsOffset));
+    Map* map = fixed_array->map();
+    // Arrays that have been shifted in place can't be verified.
+    if (map != heap->raw_unchecked_one_pointer_filler_map() &&
+        map != heap->raw_unchecked_two_pointer_filler_map() &&
+        map != heap->free_space_map()) {
+      for (int i = 0; i < fixed_array->length(); i++) {
+        Object* current = fixed_array->get(i);
+        ASSERT(current->IsSmi() || current == heap->the_hole_value());
+      }
+    }
+  }
+#endif
+}
+
+
+MaybeObject* JSObject::EnsureCanContainNonSmiElements() {
+#if DEBUG
+  ValidateSmiOnlyElements();
+#endif
+  if (FLAG_smi_only_arrays &&
+      (map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS)) {
+    Object* obj;
+    MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
+    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+    set_map(Map::cast(obj));
+  }
+  return this;
+}
+
+
+MaybeObject* JSObject::EnsureCanContainElements(Object** objects,
+                                                uint32_t count) {
+  if (FLAG_smi_only_arrays &&
+      map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) {
+    for (uint32_t i = 0; i < count; ++i) {
+      Object* current = *objects++;
+      if (!current->IsSmi() && current != GetHeap()->the_hole_value()) {
+        return EnsureCanContainNonSmiElements();
+      }
+    }
+  }
+  return this;
+}
+
+
+MaybeObject* JSObject::EnsureCanContainElements(FixedArray* elements) {
+  if (FLAG_smi_only_arrays) {
+    Object** objects = reinterpret_cast<Object**>(
+        FIELD_ADDR(elements, elements->OffsetOfElementAt(0)));
+    return EnsureCanContainElements(objects, elements->length());
+  } else {
+    return this;
+  }
+}
+
 
 void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) {
-  ASSERT(map()->has_fast_elements() ==
+  ASSERT((map()->has_fast_elements() ||
+          map()->has_fast_smi_only_elements()) ==
          (value->map() == GetHeap()->fixed_array_map() ||
           value->map() == GetHeap()->fixed_cow_array_map()));
   ASSERT(map()->has_fast_double_elements() ==
          value->IsFixedDoubleArray());
   ASSERT(value->HasValidElements());
+#ifdef DEBUG
+  ValidateSmiOnlyElements();
+#endif
   WRITE_FIELD(this, kElementsOffset, value);
   CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, value, mode);
 }
@@ -1320,7 +1388,7 @@ void JSObject::initialize_properties() {
 
 
 void JSObject::initialize_elements() {
-  ASSERT(map()->has_fast_elements());
+  ASSERT(map()->has_fast_elements() || map()->has_fast_smi_only_elements());
   ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array()));
   WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array());
 }
@@ -1328,9 +1396,11 @@ void JSObject::initialize_elements() {
 
 MaybeObject* JSObject::ResetElements() {
   Object* obj;
-  { MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
-    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-  }
+  ElementsKind elements_kind = FLAG_smi_only_arrays
+      ? FAST_SMI_ONLY_ELEMENTS
+      : FAST_ELEMENTS;
+  MaybeObject* maybe_obj = GetElementsTransitionMap(elements_kind);
+  if (!maybe_obj->ToObject(&obj)) return maybe_obj;
   set_map(Map::cast(obj));
   initialize_elements();
   return this;
@@ -1686,7 +1756,7 @@ void FixedDoubleArray::Initialize(FixedDoubleArray* from) {
 
 void FixedDoubleArray::Initialize(FixedArray* from) {
   int old_length = from->length();
-  ASSERT(old_length < length());
+  ASSERT(old_length <= length());
   for (int i = 0; i < old_length; i++) {
     Object* hole_or_object = from->get(i);
     if (hole_or_object->IsTheHole()) {
@@ -3957,15 +4027,20 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) {
 
 ElementsKind JSObject::GetElementsKind() {
   ElementsKind kind = map()->elements_kind();
-  ASSERT((kind == FAST_ELEMENTS &&
-          (elements()->map() == GetHeap()->fixed_array_map() ||
-           elements()->map() == GetHeap()->fixed_cow_array_map())) ||
+#if DEBUG
+  FixedArrayBase* fixed_array =
+      reinterpret_cast<FixedArrayBase*>(READ_FIELD(this, kElementsOffset));
+  Map* map = fixed_array->map();
+    ASSERT(((kind == FAST_ELEMENTS || kind == FAST_SMI_ONLY_ELEMENTS) &&
+          (map == GetHeap()->fixed_array_map() ||
+           map == GetHeap()->fixed_cow_array_map())) ||
          (kind == FAST_DOUBLE_ELEMENTS &&
-          elements()->IsFixedDoubleArray()) ||
+          fixed_array->IsFixedDoubleArray()) ||
          (kind == DICTIONARY_ELEMENTS &&
-          elements()->IsFixedArray() &&
-          elements()->IsDictionary()) ||
+          fixed_array->IsFixedArray() &&
+          fixed_array->IsDictionary()) ||
          (kind > DICTIONARY_ELEMENTS));
+#endif
   return kind;
 }
 
@@ -3980,6 +4055,18 @@ bool JSObject::HasFastElements() {
 }
 
 
+bool JSObject::HasFastSmiOnlyElements() {
+  return GetElementsKind() == FAST_SMI_ONLY_ELEMENTS;
+}
+
+
+bool JSObject::HasFastTypeElements() {
+  ElementsKind elements_kind = GetElementsKind();
+  return elements_kind == FAST_SMI_ONLY_ELEMENTS ||
+      elements_kind == FAST_ELEMENTS;
+}
+
+
 bool JSObject::HasFastDoubleElements() {
   return GetElementsKind() == FAST_DOUBLE_ELEMENTS;
 }
@@ -3990,6 +4077,11 @@ bool JSObject::HasDictionaryElements() {
 }
 
 
+bool JSObject::HasNonStrictArgumentsElements() {
+  return GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS;
+}
+
+
 bool JSObject::HasExternalArrayElements() {
   HeapObject* array = elements();
   ASSERT(array != NULL);
@@ -4041,7 +4133,7 @@ bool JSObject::AllowsSetElementsLength() {
 
 
 MaybeObject* JSObject::EnsureWritableFastElements() {
-  ASSERT(HasFastElements());
+  ASSERT(HasFastTypeElements());
   FixedArray* elems = FixedArray::cast(elements());
   Isolate* isolate = GetIsolate();
   if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems;
@@ -4409,7 +4501,7 @@ void Map::ClearCodeCache(Heap* heap) {
 
 
 void JSArray::EnsureSize(int required_size) {
-  ASSERT(HasFastElements());
+  ASSERT(HasFastTypeElements());
   FixedArray* elts = FixedArray::cast(elements());
   const int kArraySizeThatFitsComfortablyInNewSpace = 128;
   if (elts->length() < required_size) {
@@ -4432,9 +4524,12 @@ void JSArray::set_length(Smi* length) {
 }
 
 
-void JSArray::SetContent(FixedArray* storage) {
+MaybeObject* JSArray::SetContent(FixedArray* storage) {
+  MaybeObject* maybe_object = EnsureCanContainElements(storage);
+  if (maybe_object->IsFailure()) return maybe_object;
   set_length(Smi::FromInt(storage->length()));
   set_elements(storage);
+  return this;
 }
 
 
index 7d8f133..bb80392 100644 (file)
@@ -82,6 +82,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
     case HEAP_NUMBER_TYPE:
       HeapNumber::cast(this)->HeapNumberPrint(out);
       break;
+    case FIXED_DOUBLE_ARRAY_TYPE:
+      FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out);
+      break;
     case FIXED_ARRAY_TYPE:
       FixedArray::cast(this)->FixedArrayPrint(out);
       break;
@@ -242,6 +245,54 @@ void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) {
 }
 
 
+static void PrintElementsKind(FILE* out, ElementsKind kind) {
+  switch (kind) {
+    case FAST_SMI_ONLY_ELEMENTS:
+      PrintF(out, "FAST_SMI_ONLY_ELEMENTS");
+      break;
+    case FAST_ELEMENTS:
+      PrintF(out, "FAST_ELEMENTS");
+      break;
+    case FAST_DOUBLE_ELEMENTS:
+      PrintF(out, "FAST_DOUBLE_ELEMENTS");
+      break;
+    case DICTIONARY_ELEMENTS:
+      PrintF(out, "DICTIONARY_ELEMENTS");
+      break;
+    case NON_STRICT_ARGUMENTS_ELEMENTS:
+      PrintF(out, "NON_STRICT_ARGUMENTS_ELEMENTS");
+      break;
+    case EXTERNAL_BYTE_ELEMENTS:
+      PrintF(out, "EXTERNAL_BYTE_ELEMENTS");
+      break;
+    case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+      PrintF(out, "EXTERNAL_UNSIGNED_BYTE_ELEMENTS");
+      break;
+    case EXTERNAL_SHORT_ELEMENTS:
+      PrintF(out, "EXTERNAL_SHORT_ELEMENTS");
+      break;
+    case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+      PrintF(out, "EXTERNAL_UNSIGNED_SHORT_ELEMENTS");
+      break;
+    case EXTERNAL_INT_ELEMENTS:
+      PrintF(out, "EXTERNAL_INT_ELEMENTS");
+      break;
+    case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+      PrintF(out, "EXTERNAL_UNSIGNED_INT_ELEMENTS");
+      break;
+    case EXTERNAL_FLOAT_ELEMENTS:
+      PrintF(out, "EXTERNAL_FLOAT_ELEMENTS");
+      break;
+    case EXTERNAL_DOUBLE_ELEMENTS:
+      PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
+      break;
+    case EXTERNAL_PIXEL_ELEMENTS:
+      PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
+      break;
+  }
+}
+
+
 void JSObject::PrintProperties(FILE* out) {
   if (HasFastProperties()) {
     DescriptorArray* descs = map()->instance_descriptors();
@@ -265,16 +316,19 @@ void JSObject::PrintProperties(FILE* out) {
           PrintF(out, " (callback)\n");
           break;
         case ELEMENTS_TRANSITION:
-          PrintF(out, " (elements transition)\n");
+          PrintF(out, "(elements transition to ");
+          PrintElementsKind(out,
+                            Map::cast(descs->GetValue(i))->elements_kind());
+          PrintF(out, ")\n");
           break;
         case MAP_TRANSITION:
-          PrintF(out, " (map transition)\n");
+          PrintF(out, "(map transition)\n");
           break;
         case CONSTANT_TRANSITION:
-          PrintF(out, " (constant transition)\n");
+          PrintF(out, "(constant transition)\n");
           break;
         case NULL_DESCRIPTOR:
-          PrintF(out, " (null descriptor)\n");
+          PrintF(out, "(null descriptor)\n");
           break;
         default:
           UNREACHABLE();
@@ -288,7 +342,10 @@ void JSObject::PrintProperties(FILE* out) {
 
 
 void JSObject::PrintElements(FILE* out) {
-  switch (GetElementsKind()) {
+  // Don't call GetElementsKind, its validation code can cause the printer to
+  // fail when debugging.
+  switch (map()->elements_kind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       // Print in array notation for non-sparse arrays.
       FixedArray* p = FixedArray::cast(elements());
@@ -396,8 +453,13 @@ void JSObject::PrintElements(FILE* out) {
 
 void JSObject::JSObjectPrint(FILE* out) {
   PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
-  PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
-  PrintF(out, " - prototype = %p\n", reinterpret_cast<void*>(GetPrototype()));
+  PrintF(out, " - map = %p [", reinterpret_cast<void*>(map()));
+  // Don't call GetElementsKind, its validation code can cause the printer to
+  // fail when debugging.
+  PrintElementsKind(out, this->map()->elements_kind());
+  PrintF(out,
+         "]\n - prototype = %p\n",
+         reinterpret_cast<void*>(GetPrototype()));
   PrintF(out, " {\n");
   PrintProperties(out);
   PrintElements(out);
@@ -528,6 +590,16 @@ void FixedArray::FixedArrayPrint(FILE* out) {
 }
 
 
+void FixedDoubleArray::FixedDoubleArrayPrint(FILE* out) {
+  HeapObject::PrintHeader(out, "FixedDoubleArray");
+  PrintF(out, " - length: %d", length());
+  for (int i = 0; i < length(); i++) {
+    PrintF(out, "\n  [%d]: %g", i, get_scalar(i));
+  }
+  PrintF(out, "\n");
+}
+
+
 void JSValue::JSValuePrint(FILE* out) {
   HeapObject::PrintHeader(out, "ValueObject");
   value()->Print(out);
index 94671d0..f894bc5 100644 (file)
@@ -3063,6 +3063,7 @@ MaybeObject* JSObject::NormalizeElements() {
   if (array->IsDictionary()) return array;
 
   ASSERT(HasFastElements() ||
+         HasFastSmiOnlyElements() ||
          HasFastDoubleElements() ||
          HasFastArgumentsElements());
   // Compute the effective length and allocate a new backing store.
@@ -3097,7 +3098,8 @@ MaybeObject* JSObject::NormalizeElements() {
         if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
       }
     } else {
-      ASSERT(old_map->has_fast_elements());
+      ASSERT(old_map->has_fast_elements() ||
+             old_map->has_fast_smi_only_elements());
       value = FixedArray::cast(array)->get(i);
     }
     PropertyDetails details = PropertyDetails(NONE, NORMAL);
@@ -3412,7 +3414,8 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
 bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
                                             ElementsKind kind,
                                             Object* object) {
-  ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS);
+  ASSERT(kind == FAST_ELEMENTS ||
+         kind == DICTIONARY_ELEMENTS);
   if (kind == FAST_ELEMENTS) {
     int length = IsJSArray()
         ? Smi::cast(JSArray::cast(this)->length())->value()
@@ -3467,6 +3470,8 @@ bool JSObject::ReferencesObject(Object* obj) {
       // Raw pixels and external arrays do not reference other
       // objects.
       break;
+    case FAST_SMI_ONLY_ELEMENTS:
+      break;
     case FAST_ELEMENTS:
     case DICTIONARY_ELEMENTS: {
       FixedArray* elements = FixedArray::cast(this->elements());
@@ -3758,6 +3763,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name,
 
   if (is_element) {
     switch (GetElementsKind()) {
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
         break;
@@ -4007,6 +4013,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
 
     // Accessors overwrite previous callbacks (cf. with getters/setters).
     switch (GetElementsKind()) {
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
         break;
@@ -7416,8 +7423,10 @@ static void CopySlowElementsToFast(NumberDictionary* source,
 }
 
 
-MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
-                                                        int length) {
+MaybeObject* JSObject::SetFastElementsCapacityAndLength(
+    int capacity,
+    int length,
+    SetFastElementsCapacityMode set_capacity_mode) {
   Heap* heap = GetHeap();
   // We should never end in here with a pixel or external array.
   ASSERT(!HasExternalArrayElements());
@@ -7434,15 +7443,25 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
   Map* new_map = NULL;
   if (elements()->map() != heap->non_strict_arguments_elements_map()) {
     Object* object;
-    MaybeObject* maybe = GetElementsTransitionMap(FAST_ELEMENTS);
+    bool has_fast_smi_only_elements =
+        FLAG_smi_only_arrays &&
+        (set_capacity_mode == kAllowSmiOnlyElements) &&
+        (elements()->map()->has_fast_smi_only_elements() ||
+         elements() == heap->empty_fixed_array());
+    ElementsKind elements_kind = has_fast_smi_only_elements
+        ? FAST_SMI_ONLY_ELEMENTS
+        : FAST_ELEMENTS;
+    MaybeObject* maybe = GetElementsTransitionMap(elements_kind);
     if (!maybe->ToObject(&object)) return maybe;
     new_map = Map::cast(object);
   }
 
-  switch (GetElementsKind()) {
+  ElementsKind elements_kind = GetElementsKind();
+  switch (elements_kind) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       AssertNoAllocation no_gc;
-      WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
+      WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc));
       CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode);
       set_map(new_map);
       set_elements(new_elements);
@@ -7545,6 +7564,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
 
   AssertNoAllocation no_gc;
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       elems->Initialize(FixedArray::cast(elements()));
       break;
@@ -7582,8 +7602,9 @@ MaybeObject* JSObject::SetSlowElements(Object* len) {
   uint32_t new_length = static_cast<uint32_t>(len->Number());
 
   switch (GetElementsKind()) {
-    case FAST_ELEMENTS: {
-    case FAST_DOUBLE_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
+    case FAST_ELEMENTS:
+    case FAST_DOUBLE_ELEMENTS: {
       // Make sure we never try to shrink dense arrays into sparse arrays.
       ASSERT(static_cast<uint32_t>(
           FixedArrayBase::cast(elements())->length()) <= new_length);
@@ -7649,7 +7670,7 @@ void JSArray::Expand(int required_size) {
   Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size);
   // Can't use this any more now because we may have had a GC!
   for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
-  self->SetContent(*new_backing);
+  GetIsolate()->factory()->SetContent(self, new_backing);
 }
 
 
@@ -7672,13 +7693,15 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
     if (value < 0) return ArrayLengthRangeError(GetHeap());
     ElementsKind elements_kind = GetElementsKind();
     switch (elements_kind) {
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS: {
         int old_capacity = FixedArrayBase::cast(elements())->length();
         if (value <= old_capacity) {
           if (IsJSArray()) {
             Object* obj;
-            if (elements_kind == FAST_ELEMENTS) {
+            if (elements_kind == FAST_ELEMENTS ||
+                elements_kind == FAST_SMI_ONLY_ELEMENTS) {
               MaybeObject* maybe_obj = EnsureWritableFastElements();
               if (!maybe_obj->ToObject(&obj)) return maybe_obj;
             }
@@ -7689,7 +7712,8 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
               } else {
                 Address filler_start;
                 int filler_size;
-                if (GetElementsKind() == FAST_ELEMENTS) {
+                if (elements_kind == FAST_ELEMENTS ||
+                    elements_kind == FAST_SMI_ONLY_ELEMENTS) {
                   FixedArray* fast_elements = FixedArray::cast(elements());
                   fast_elements->set_length(value);
                   filler_start = fast_elements->address() +
@@ -7709,13 +7733,14 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
             } else {
               // Otherwise, fill the unused tail with holes.
               int old_length = FastD2I(JSArray::cast(this)->length()->Number());
-              if (GetElementsKind() == FAST_ELEMENTS) {
+              if (elements_kind == FAST_ELEMENTS ||
+                  elements_kind == FAST_SMI_ONLY_ELEMENTS) {
                 FixedArray* fast_elements = FixedArray::cast(elements());
                 for (int i = value; i < old_length; i++) {
                   fast_elements->set_the_hole(i);
                 }
               } else {
-                ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
+                ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
                 FixedDoubleArray* fast_double_elements =
                     FixedDoubleArray::cast(elements());
                 for (int i = value; i < old_length; i++) {
@@ -7731,10 +7756,17 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
         int new_capacity = value > min ? value : min;
         if (!ShouldConvertToSlowElements(new_capacity)) {
           MaybeObject* result;
-          if (GetElementsKind() == FAST_ELEMENTS) {
-            result = SetFastElementsCapacityAndLength(new_capacity, value);
+          if (elements_kind == FAST_ELEMENTS ||
+              elements_kind == FAST_SMI_ONLY_ELEMENTS) {
+            SetFastElementsCapacityMode set_capacity_mode =
+                elements_kind == FAST_SMI_ONLY_ELEMENTS
+                    ? kAllowSmiOnlyElements
+                    : kDontAllowSmiOnlyElements;
+            result = SetFastElementsCapacityAndLength(new_capacity,
+                                                      value,
+                                                      set_capacity_mode);
           }  else {
-            ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
+            ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
             result = SetFastDoubleElementsCapacityAndLength(new_capacity,
                                                             value);
           }
@@ -7791,10 +7823,13 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
   // len is not a number so make the array size one and
   // set only element to len.
   Object* obj;
-  { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
-    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-  }
+  MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
+  if (!maybe_obj->ToObject(&obj)) return maybe_obj;
   FixedArray::cast(obj)->set(0, len);
+
+  maybe_obj = EnsureCanContainElements(&len, 1);
+  if (maybe_obj->IsFailure()) return maybe_obj;
+
   if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
   set_elements(FixedArray::cast(obj));
   return this;
@@ -7942,8 +7977,16 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
 }
 
 
+MaybeObject* JSObject::EnsureCanContainElements(Arguments* args,
+                                                uint32_t first_arg,
+                                                uint32_t arg_count) {
+  return EnsureCanContainElements(args->arguments() - first_arg, arg_count);
+}
+
+
 bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) {
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       uint32_t length = IsJSArray() ?
           static_cast<uint32_t>
@@ -8085,6 +8128,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
   }
 
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       uint32_t length = IsJSArray() ?
           static_cast<uint32_t>
@@ -8199,6 +8243,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
 
   ElementsKind kind = GetElementsKind();
   switch (kind) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       uint32_t length = IsJSArray() ?
           static_cast<uint32_t>
@@ -8453,7 +8498,8 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
                                       Object* value,
                                       StrictModeFlag strict_mode,
                                       bool check_prototype) {
-  ASSERT(HasFastElements() || HasFastArgumentsElements());
+  ASSERT(HasFastTypeElements() ||
+         HasFastArgumentsElements());
 
   FixedArray* backing_store = FixedArray::cast(elements());
   if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) {
@@ -8478,6 +8524,24 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
 
   // Check whether there is extra space in fixed array.
   if (index < length) {
+    if (HasFastSmiOnlyElements()) {
+      if (!value->IsSmi()) {
+        // If the value is a number, transition from smi-only to
+        // FastDoubleElements.
+        if (value->IsNumber()) {
+          MaybeObject* maybe =
+              SetFastDoubleElementsCapacityAndLength(length, length);
+          if (maybe->IsFailure()) return maybe;
+          FixedDoubleArray::cast(elements())->set(index, value->Number());
+          return value;
+        }
+        // Value is not a number, transition to generic fast elements.
+        MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS);
+        Map* new_map;
+        if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map;
+        set_map(new_map);
+      }
+    }
     backing_store->set(index, value);
     if (IsJSArray()) {
       // Update the length of the array if needed.
@@ -8497,8 +8561,14 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
     if (!ShouldConvertToSlowElements(new_capacity)) {
       ASSERT(static_cast<uint32_t>(new_capacity) > index);
       Object* new_elements;
+      SetFastElementsCapacityMode set_capacity_mode =
+          value->IsSmi() && HasFastSmiOnlyElements()
+              ? kAllowSmiOnlyElements
+              : kDontAllowSmiOnlyElements;
       MaybeObject* maybe =
-          SetFastElementsCapacityAndLength(new_capacity, index + 1);
+          SetFastElementsCapacityAndLength(new_capacity,
+                                           index + 1,
+                                           set_capacity_mode);
       if (!maybe->ToObject(&new_elements)) return maybe;
       FixedArray::cast(new_elements)->set(index, value);
       return value;
@@ -8604,7 +8674,9 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
     }
     MaybeObject* result = CanConvertToFastDoubleElements()
         ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
-        : SetFastElementsCapacityAndLength(new_length, new_length);
+        : SetFastElementsCapacityAndLength(new_length,
+                                           new_length,
+                                           kDontAllowSmiOnlyElements);
     if (result->IsFailure()) return result;
 #ifdef DEBUG
     if (FLAG_trace_normalization) {
@@ -8648,10 +8720,15 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
     if (IsJSArray()) {
       CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
     }
-    MaybeObject* maybe_obj =
-        SetFastElementsCapacityAndLength(elms_length, length);
+    MaybeObject* maybe_obj = SetFastElementsCapacityAndLength(
+        elms_length,
+        length,
+        kDontAllowSmiOnlyElements);
     if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-    return SetFastElement(index, value, strict_mode, check_prototype);
+    return SetFastElement(index,
+                          value,
+                          strict_mode,
+                          check_prototype);
   }
 
   double double_value = value_is_smi
@@ -8759,6 +8836,7 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
                                                     bool check_prototype) {
   Isolate* isolate = GetIsolate();
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS:
       return SetFastElement(index, value, strict_mode, check_prototype);
     case FAST_DOUBLE_ELEMENTS:
@@ -8921,6 +8999,7 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
         break;
       }
       // Fall through.
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS:
       backing_store = FixedArray::cast(backing_store_base);
       *capacity = backing_store->length();
@@ -9196,6 +9275,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) {
   if (this->IsStringObjectWithCharacterAt(index)) return true;
 
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       uint32_t length = IsJSArray() ?
           static_cast<uint32_t>(
@@ -9435,6 +9515,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage,
                                   PropertyAttributes filter) {
   int counter = 0;
   switch (GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       int length = IsJSArray() ?
           Smi::cast(JSArray::cast(this)->length())->value() :
@@ -10337,7 +10418,8 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
       if (!maybe_obj->ToObject(&obj)) return maybe_obj;
     }
   }
-  ASSERT(HasFastElements() || HasFastDoubleElements());
+  ASSERT(HasFastTypeElements() ||
+         HasFastDoubleElements());
 
   // Collect holes at the end, undefined before that and the rest at the
   // start, and return the number of non-hole, non-undefined values.
index 38e9add..9711245 100644 (file)
@@ -137,8 +137,13 @@ namespace v8 {
 namespace internal {
 
 enum ElementsKind {
-  // The "fast" kind for tagged values. Must be first to make it possible
-  // to efficiently check maps if they have fast elements.
+  // The "fast" kind for elements that only contain SMI values. Must be first
+  // to make it possible to efficiently check maps for this kind.
+  FAST_SMI_ONLY_ELEMENTS,
+
+  // The "fast" kind for tagged values. Must be second to make it possible to
+  // efficiently check maps for this and the FAST_SMI_ONLY_ELEMENTS kind
+  // together at once.
   FAST_ELEMENTS,
 
   // The "fast" kind for unwrapped, non-tagged double values.
@@ -161,7 +166,7 @@ enum ElementsKind {
   // Derived constants from ElementsKind
   FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS,
   LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS,
-  FIRST_ELEMENTS_KIND = FAST_ELEMENTS,
+  FIRST_ELEMENTS_KIND = FAST_SMI_ONLY_ELEMENTS,
   LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS
 };
 
@@ -1432,8 +1437,14 @@ class JSObject: public JSReceiver {
   MUST_USE_RESULT inline MaybeObject* ResetElements();
   inline ElementsKind GetElementsKind();
   inline ElementsAccessor* GetElementsAccessor();
+  inline bool HasFastSmiOnlyElements();
   inline bool HasFastElements();
+  // Returns if an object has either FAST_ELEMENT or FAST_SMI_ONLY_ELEMENT
+  // elements.  TODO(danno): Rename HasFastTypeElements to HasFastElements() and
+  // HasFastElements to HasFastObjectElements.
+  inline bool HasFastTypeElements();
   inline bool HasFastDoubleElements();
+  inline bool HasNonStrictArgumentsElements();
   inline bool HasDictionaryElements();
   inline bool HasExternalPixelElements();
   inline bool HasExternalArrayElements();
@@ -1608,6 +1619,19 @@ class JSObject: public JSReceiver {
   // Tests for the fast common case for property enumeration.
   bool IsSimpleEnum();
 
+  inline void ValidateSmiOnlyElements();
+
+  // Makes sure that this object can contain non-smi Object as elements.
+  inline MaybeObject* EnsureCanContainNonSmiElements();
+
+  // Makes sure that this object can contain the specified elements.
+  inline MaybeObject* EnsureCanContainElements(Object** elements,
+                                               uint32_t count);
+  inline MaybeObject* EnsureCanContainElements(FixedArray* elements);
+  MaybeObject* EnsureCanContainElements(Arguments* arguments,
+                                        uint32_t first_arg,
+                                        uint32_t arg_count);
+
   // Do we want to keep the elements in fast case when increasing the
   // capacity?
   bool ShouldConvertToSlowElements(int new_capacity);
@@ -1656,6 +1680,7 @@ class JSObject: public JSReceiver {
                                               Object* value,
                                               StrictModeFlag strict_mode,
                                               bool check_prototype);
+
   MUST_USE_RESULT MaybeObject* SetDictionaryElement(uint32_t index,
                                                     Object* value,
                                                     StrictModeFlag strict_mode,
@@ -1678,11 +1703,18 @@ class JSObject: public JSReceiver {
   // The undefined object if index is out of bounds.
   MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index);
 
+  enum SetFastElementsCapacityMode {
+    kAllowSmiOnlyElements,
+    kDontAllowSmiOnlyElements
+  };
+
   // Replace the elements' backing store with fast elements of the given
   // capacity.  Update the length for JSArrays.  Returns the new backing
   // store.
-  MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity,
-                                                                int length);
+  MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(
+      int capacity,
+      int length,
+      SetFastElementsCapacityMode set_capacity_mode);
   MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength(
       int capacity,
       int length);
@@ -3980,8 +4012,12 @@ class Map: public HeapObject {
         (bit_field2() & kElementsKindMask) >> kElementsKindShift);
   }
 
+  // Tells whether the instance has fast elements that are only Smis.
+  inline bool has_fast_smi_only_elements() {
+    return elements_kind() == FAST_SMI_ONLY_ELEMENTS;
+  }
+
   // Tells whether the instance has fast elements.
-  // Equivalent to instance->GetElementsKind() == FAST_ELEMENTS.
   inline bool has_fast_elements() {
     return elements_kind() == FAST_ELEMENTS;
   }
@@ -3990,6 +4026,10 @@ class Map: public HeapObject {
     return elements_kind() == FAST_DOUBLE_ELEMENTS;
   }
 
+  inline bool has_non_strict_arguments_elements() {
+    return elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS;
+  }
+
   inline bool has_external_array_elements() {
     ElementsKind kind(elements_kind());
     return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
@@ -4237,7 +4277,7 @@ class Map: public HeapObject {
   static const int kStringWrapperSafeForDefaultValueOf = 2;
   static const int kAttachedToSharedFunctionInfo = 3;
   // No bits can be used after kElementsKindFirstBit, they are all reserved for
-  // storing ElementKind.  for anything other than storing the ElementKind.
+  // storing ElementKind.
   static const int kElementsKindShift = 4;
   static const int kElementsKindBitCount = 4;
 
@@ -4246,6 +4286,9 @@ class Map: public HeapObject {
       ((1 << (kElementsKindShift + kElementsKindBitCount)) - 1);
   static const int8_t kMaximumBitField2FastElementValue = static_cast<int8_t>(
       (FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1;
+  static const int8_t kMaximumBitField2FastSmiOnlyElementValue =
+      static_cast<int8_t>((FAST_SMI_ONLY_ELEMENTS + 1) <<
+                          Map::kElementsKindShift) - 1;
 
   // Bit positions for bit field 3
   static const int kIsShared = 0;
@@ -6861,7 +6904,7 @@ class JSArray: public JSObject {
   MUST_USE_RESULT MaybeObject* Initialize(int capacity);
 
   // Set the content of the array to the content of storage.
-  inline void SetContent(FixedArray* storage);
+  inline MaybeObject* SetContent(FixedArray* storage);
 
   // Casting.
   static inline JSArray* cast(Object* obj);
index a51fcd3..cb63072 100644 (file)
@@ -177,6 +177,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate,
   // Pixel elements cannot be created using an object literal.
   ASSERT(!copy->HasExternalArrayElements());
   switch (copy->GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       FixedArray* elements = FixedArray::cast(copy->elements());
       if (elements->map() == heap->fixed_cow_array_map()) {
@@ -189,6 +190,9 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate,
       } else {
         for (int i = 0; i < elements->length(); i++) {
           Object* value = elements->get(i);
+          ASSERT(value->IsSmi() ||
+                 value->IsTheHole() ||
+                 (copy->GetElementsKind() == FAST_ELEMENTS));
           if (value->IsJSObject()) {
             JSObject* js_object = JSObject::cast(value);
             { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate,
@@ -432,16 +436,28 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
       is_cow ? elements : isolate->factory()->CopyFixedArray(elements);
 
   Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
+  bool has_non_smi = false;
   if (is_cow) {
-#ifdef DEBUG
     // Copy-on-write arrays must be shallow (and simple).
-    for (int i = 0; i < content->length(); i++) {
-      ASSERT(!content->get(i)->IsFixedArray());
-    }
+    if (FLAG_smi_only_arrays) {
+      for (int i = 0; i < content->length(); i++) {
+        Object* current = content->get(i);
+        ASSERT(!current->IsFixedArray());
+        if (!current->IsSmi()) {
+          has_non_smi = true;
+        }
+      }
+    } else {
+#if DEBUG
+      for (int i = 0; i < content->length(); i++) {
+        ASSERT(!content->get(i)->IsFixedArray());
+      }
 #endif
+    }
   } else {
     for (int i = 0; i < content->length(); i++) {
-      if (content->get(i)->IsFixedArray()) {
+      Object* current = content->get(i);
+      if (current->IsFixedArray()) {
         // The value contains the constant_properties of a
         // simple object or array literal.
         Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
@@ -449,12 +465,25 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
             CreateLiteralBoilerplate(isolate, literals, fa);
         if (result.is_null()) return result;
         content->set(i, *result);
+        has_non_smi = true;
+      } else {
+        if (!current->IsSmi()) {
+          has_non_smi = true;
+        }
       }
     }
   }
 
   // Set the elements.
-  Handle<JSArray>::cast(object)->SetContent(*content);
+  Handle<JSArray> js_object(Handle<JSArray>::cast(object));
+  isolate->factory()->SetContent(js_object, content);
+
+  if (FLAG_smi_only_arrays) {
+    if (has_non_smi && js_object->HasFastSmiOnlyElements()) {
+      isolate->factory()->EnsureCanContainNonSmiElements(js_object);
+    }
+  }
+
   return object;
 }
 
@@ -1631,6 +1660,19 @@ RUNTIME_FUNCTION(MaybeObject*,
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NonSmiElementStored) {
+  ASSERT(args.length() == 1);
+  CONVERT_ARG_CHECKED(JSObject, object, 0);
+  if (FLAG_smi_only_arrays && object->HasFastSmiOnlyElements()) {
+    MaybeObject* maybe_map = object->GetElementsTransitionMap(FAST_ELEMENTS);
+    Map* map;
+    if (!maybe_map->To<Map>(&map)) return maybe_map;
+    object->set_map(Map::cast(map));
+  }
+  return *object;
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 4);
@@ -2214,7 +2256,8 @@ class FixedArrayBuilder {
  public:
   explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
       : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
-        length_(0) {
+        length_(0),
+        has_non_smi_elements_(false) {
     // Require a non-zero initial size. Ensures that doubling the size to
     // extend the array will work.
     ASSERT(initial_capacity > 0);
@@ -2222,7 +2265,8 @@ class FixedArrayBuilder {
 
   explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
       : array_(backing_store),
-        length_(0) {
+        length_(0),
+        has_non_smi_elements_(false) {
     // Require a non-zero initial size. Ensures that doubling the size to
     // extend the array will work.
     ASSERT(backing_store->length() > 0);
@@ -2250,12 +2294,15 @@ class FixedArrayBuilder {
   }
 
   void Add(Object* value) {
+    ASSERT(!value->IsSmi());
     ASSERT(length_ < capacity());
     array_->set(length_, value);
     length_++;
+    has_non_smi_elements_ = true;
   }
 
   void Add(Smi* value) {
+    ASSERT(value->IsSmi());
     ASSERT(length_ < capacity());
     array_->set(length_, value);
     length_++;
@@ -2280,7 +2327,7 @@ class FixedArrayBuilder {
   }
 
   Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
-    target_array->set_elements(*array_);
+    FACTORY->SetContent(target_array, array_);
     target_array->set_length(Smi::FromInt(length_));
     return target_array;
   }
@@ -2288,6 +2335,7 @@ class FixedArrayBuilder {
  private:
   Handle<FixedArray> array_;
   int length_;
+  bool has_non_smi_elements_;
 };
 
 
@@ -6121,6 +6169,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
   int part_count = indices.length();
 
   Handle<JSArray> result = isolate->factory()->NewJSArray(part_count);
+  MaybeObject* maybe_result = result->EnsureCanContainNonSmiElements();
+  if (maybe_result->IsFailure()) return maybe_result;
   result->set_length(Smi::FromInt(part_count));
 
   ASSERT(result->HasFastElements());
@@ -6495,6 +6545,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
   // This assumption is used by the slice encoding in one or two smis.
   ASSERT(Smi::kMaxValue >= String::kMaxLength);
 
+  MaybeObject* maybe_result = array->EnsureCanContainNonSmiElements();
+  if (maybe_result->IsFailure()) return maybe_result;
+
   int special_length = special->length();
   if (!array->HasFastElements()) {
     return isolate->Throw(isolate->heap()->illegal_argument_symbol());
@@ -6722,7 +6775,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 3);
   CONVERT_CHECKED(JSArray, elements_array, args[0]);
-  RUNTIME_ASSERT(elements_array->HasFastElements());
+  RUNTIME_ASSERT(elements_array->HasFastElements() ||
+                 elements_array->HasFastSmiOnlyElements());
   CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
   CONVERT_CHECKED(String, separator, args[2]);
   // elements_array is fast-mode JSarray of alternating positions
@@ -7889,7 +7943,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
   int bound_argc = 0;
   if (!args[1]->IsNull()) {
     CONVERT_ARG_CHECKED(JSArray, params, 1);
-    RUNTIME_ASSERT(params->HasFastElements());
+    RUNTIME_ASSERT(params->HasFastTypeElements());
     bound_args = Handle<FixedArray>(FixedArray::cast(params->elements()));
     bound_argc = Smi::cast(params->length())->value();
   }
@@ -8984,6 +9038,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) {
   FlattenString(str);
 
   CONVERT_ARG_CHECKED(JSArray, output, 1);
+
+  MaybeObject* maybe_result_array =
+      output->EnsureCanContainNonSmiElements();
+  if (maybe_result_array->IsFailure()) return maybe_result_array;
   RUNTIME_ASSERT(output->HasFastElements());
 
   AssertNoAllocation no_allocation;
@@ -9287,7 +9345,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) {
   ASSERT(args.length() == 2);
   CONVERT_CHECKED(JSArray, array, args[0]);
   CONVERT_CHECKED(JSObject, element, args[1]);
-  RUNTIME_ASSERT(array->HasFastElements());
+  RUNTIME_ASSERT(array->HasFastElements() || array->HasFastSmiOnlyElements());
   int length = Smi::cast(array->length())->value();
   FixedArray* elements = FixedArray::cast(array->elements());
   for (int i = 0; i < length; i++) {
@@ -9518,6 +9576,7 @@ static void CollectElementIndices(Handle<JSObject> object,
                                   List<uint32_t>* indices) {
   ElementsKind kind = object->GetElementsKind();
   switch (kind) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       Handle<FixedArray> elements(FixedArray::cast(object->elements()));
       uint32_t length = static_cast<uint32_t>(elements->length());
@@ -9637,6 +9696,7 @@ static bool IterateElements(Isolate* isolate,
                             ArrayConcatVisitor* visitor) {
   uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
   switch (receiver->GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_ELEMENTS: {
       // Run through the elements FixedArray and use HasElement and GetElement
       // to check the prototype for missing elements.
@@ -9960,7 +10020,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) {
     }
     return *isolate->factory()->NewJSArrayWithElements(keys);
   } else {
-    ASSERT(array->HasFastElements() || array->HasFastDoubleElements());
+    ASSERT(array->HasFastElements() ||
+           array->HasFastSmiOnlyElements() ||
+           array->HasFastDoubleElements());
     Handle<FixedArray> single_interval = isolate->factory()->NewFixedArray(2);
     // -1 means start of array.
     single_interval->set(0, Smi::FromInt(-1));
@@ -11870,7 +11932,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) {
   // Return result as a JS array.
   Handle<JSObject> result =
       isolate->factory()->NewJSObject(isolate->array_function());
-  Handle<JSArray>::cast(result)->SetContent(*instances);
+  isolate->factory()->SetContent(Handle<JSArray>::cast(result), instances);
   return *result;
 }
 
@@ -11998,12 +12060,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) {
 
   // Return result as JS array.
   Object* result;
-  MaybeObject* maybe_result = isolate->heap()->AllocateJSObject(
+  MaybeObject* maybe_result = isolate->heap()->AllocateJSObject(
       isolate->context()->global_context()->array_function());
-    if (!maybe_result->ToObject(&result)) return maybe_result;
-  }
-  JSArray::cast(result)->SetContent(instances);
-  return result;
+  if (!maybe_result->ToObject(&result)) return maybe_result;
+  return JSArray::cast(result)->SetContent(instances);
 }
 
 
@@ -12084,8 +12144,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) {
           isolate->context()->global_context()->array_function());
     if (!maybe_result->ToObject(&result)) return maybe_result;
   }
-  JSArray::cast(result)->SetContent(instances);
-  return result;
+  return JSArray::cast(result)->SetContent(instances);
 }
 
 
@@ -13057,6 +13116,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IS_VAR) {
     return isolate->heap()->ToBoolean(obj->Has##Name());  \
   }
 
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOnlyElements)
 ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements)
 ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
 ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
index 1538b7d..c6f7773 100644 (file)
@@ -330,6 +330,8 @@ namespace internal {
   F(InitializeConstContextSlot, 3, 1) \
   F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
   \
+  /* Arrays */ \
+  F(NonSmiElementStored, 1, 1) \
   /* Debugging */ \
   F(DebugPrint, 1, 1) \
   F(DebugTrace, 0, 1) \
@@ -354,6 +356,7 @@ namespace internal {
   F(IS_VAR, 1, 1) \
   \
   /* expose boolean functions from objects-inl.h */ \
+  F(HasFastSmiOnlyElements, 1, 1) \
   F(HasFastElements, 1, 1) \
   F(HasFastDoubleElements, 1, 1) \
   F(HasDictionaryElements, 1, 1) \
index 18c157b..fcb58e1 100644 (file)
@@ -704,7 +704,8 @@ class KeyedStoreStubCompiler: public StubCompiler {
       CodeList* handler_ics);
 
   static void GenerateStoreFastElement(MacroAssembler* masm,
-                                       bool is_js_array);
+                                       bool is_js_array,
+                                       ElementsKind element_kind);
 
   static void GenerateStoreFastDoubleElement(MacroAssembler* masm,
                                              bool is_js_array);
index 350216a..e000fef 100644 (file)
@@ -2396,6 +2396,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
       case EXTERNAL_FLOAT_ELEMENTS:
       case EXTERNAL_DOUBLE_ELEMENTS:
       case FAST_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case DICTIONARY_ELEMENTS:
       case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3121,6 +3122,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
       case EXTERNAL_FLOAT_ELEMENTS:
       case EXTERNAL_DOUBLE_ELEMENTS:
       case FAST_ELEMENTS:
+      case FAST_SMI_ONLY_ELEMENTS:
       case FAST_DOUBLE_ELEMENTS:
       case DICTIONARY_ELEMENTS:
       case NON_STRICT_ARGUMENTS_ELEMENTS:
index e27f6c9..b73e854 100644 (file)
@@ -2648,7 +2648,8 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
 void MacroAssembler::CheckFastElements(Register map,
                                        Label* fail,
                                        Label::Distance distance) {
-  STATIC_ASSERT(FAST_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
+  STATIC_ASSERT(FAST_ELEMENTS == 1);
   cmpb(FieldOperand(map, Map::kBitField2Offset),
        Immediate(Map::kMaximumBitField2FastElementValue));
   j(above, fail, distance);
index 2ef6178..584c549 100644 (file)
@@ -3464,6 +3464,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
       __ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
       break;
     case FAST_ELEMENTS:
+    case FAST_SMI_ONLY_ELEMENTS:
     case FAST_DOUBLE_ELEMENTS:
     case DICTIONARY_ELEMENTS:
     case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3531,6 +3532,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
         case EXTERNAL_FLOAT_ELEMENTS:
         case EXTERNAL_DOUBLE_ELEMENTS:
         case FAST_ELEMENTS:
+        case FAST_SMI_ONLY_ELEMENTS:
         case FAST_DOUBLE_ELEMENTS:
         case DICTIONARY_ELEMENTS:
         case NON_STRICT_ARGUMENTS_ELEMENTS:
@@ -3662,8 +3664,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
 }
 
 
-void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
-                                                      bool is_js_array) {
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+    MacroAssembler* masm,
+    bool is_js_array,
+    ElementsKind elements_kind) {
   // ----------- S t a t e -------------
   //  -- rax    : value
   //  -- rcx    : key
@@ -3700,6 +3704,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
   __ movq(Operand(rcx, 0), rax);
   // Make sure to preserve the value in register rax.
   __ movq(rdx, rax);
+  ASSERT(elements_kind == FAST_ELEMENTS);
   __ RecordWrite(rdi, rcx, rdx, kDontSaveFPRegs);
 
   // Done.
index aeab401..9a2e017 100644 (file)
@@ -672,7 +672,8 @@ TEST(JSArray) {
   // Set array length to 0.
   ok = array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked();
   CHECK_EQ(Smi::FromInt(0), array->length());
-  CHECK(array->HasFastElements());  // Must be in fast mode.
+  // Must be in fast mode.
+  CHECK(array->HasFastTypeElements());
 
   // array[length] = name.
   ok = array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked();
index 48a029f..1949857 100644 (file)
 // Flags: --allow-natives-syntax
 // Test element kind of objects
 
+support_smi_only_arrays = %HasFastSmiOnlyElements([]);
+
 var element_kind = {
+  fast_smi_only_elements            :  0,
   fast_elements                     :  1,
   fast_double_elements              :  2,
   dictionary_elements               :  3,
@@ -45,8 +48,16 @@ var element_kind = {
 
 // We expect an object to only be of one element kind.
 function assertKind(expected, obj){
-  assertEquals(expected == element_kind.fast_elements,
-               %HasFastElements(obj));
+  if (support_smi_only_arrays) {
+    assertEquals(expected == element_kind.fast_smi_only_elements,
+                 %HasFastSmiOnlyElements(obj));
+    assertEquals(expected == element_kind.fast_elements,
+                 %HasFastElements(obj));
+  } else {
+    assertEquals(expected == element_kind.fast_elements ||
+                 expected == element_kind.fast_smi_only_elements,
+                 %HasFastElements(obj));
+  }
   assertEquals(expected == element_kind.fast_double_elements,
                %HasFastDoubleElements(obj));
   assertEquals(expected == element_kind.dictionary_elements,
@@ -80,9 +91,22 @@ me.dance = 0xD15C0;
 me.drink = 0xC0C0A;
 assertKind(element_kind.fast_elements, me);
 
+var too = [1,2,3];
+assertKind(element_kind.fast_smi_only_elements, too);
+too.dance = 0xD15C0;
+too.drink = 0xC0C0A;
+assertKind(element_kind.fast_smi_only_elements, too);
+
+// Make sure the element kind transitions from smionly when a non-smi is stored.
 var you = new Array();
+assertKind(element_kind.fast_smi_only_elements, you);
 for(i = 0; i < 1337; i++) {
-  you[i] = i;
+  var val = i;
+  if (i == 1336) {
+    assertKind(element_kind.fast_smi_only_elements, you);
+    val = new Object();
+  }
+  you[i] = val;
 }
 assertKind(element_kind.fast_elements, you);