Elements kind conversion in generated code (ia32).
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 13 Oct 2011 10:53:31 +0000 (10:53 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 13 Oct 2011 10:53:31 +0000 (10:53 +0000)
BUG=
TEST=

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

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

src/code-stubs.cc
src/code-stubs.h
src/ia32/code-stubs-ia32.cc
src/ic.cc
test/mjsunit/elements-kind.js [moved from test/mjsunit/element-kind.js with 50% similarity]
test/mjsunit/elements-transition.js [new file with mode: 0644]

index 4bc2603..78585d8 100644 (file)
@@ -415,4 +415,28 @@ bool ToBooleanStub::Types::CanBeUndetectable() const {
 }
 
 
+void FastElementsConversionStub::Generate(MacroAssembler* masm) {
+#if defined(V8_TARGET_ARCH_IA32)
+  if (to_ == FAST_ELEMENTS) {
+    if (from_ == FAST_SMI_ONLY_ELEMENTS) {
+      GenerateSmiOnlyToObject(masm);
+    } else if (from_ == FAST_DOUBLE_ELEMENTS) {
+      GenerateDoubleToObject(masm, strict_mode_);
+    } else {
+      UNREACHABLE();
+    }
+    KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
+                                                     is_jsarray_,
+                                                     FAST_ELEMENTS);
+  } else if (from_ == FAST_SMI_ONLY_ELEMENTS && to_ == FAST_DOUBLE_ELEMENTS) {
+    GenerateSmiOnlyToDouble(masm, strict_mode_);
+    KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, is_jsarray_);
+  } else {
+    UNREACHABLE();
+  }
+#else
+  KeyedStoreIC::GenerateRuntimeSetProperty(masm, strict_mode_);
+#endif  // V8_TARGET_ARCH_IA32
+}
+
 } }  // namespace v8::internal
index acfbd46..e9e600f 100644 (file)
@@ -69,7 +69,8 @@ namespace internal {
   V(KeyedLoadElement)                    \
   V(KeyedStoreElement)                   \
   V(DebuggerStatement)                   \
-  V(StringDictionaryLookup)
+  V(StringDictionaryLookup)              \
+  V(FastElementsConversion)
 
 // List of code stubs only used on ARM platforms.
 #ifdef V8_TARGET_ARCH_ARM
@@ -1025,6 +1026,47 @@ class ToBooleanStub: public CodeStub {
   Types types_;
 };
 
+
+class FastElementsConversionStub : public CodeStub {
+ public:
+  FastElementsConversionStub(ElementsKind from,
+                             ElementsKind to,
+                             bool is_jsarray,
+                             StrictModeFlag strict_mode)
+      : from_(from),
+        to_(to),
+        is_jsarray_(is_jsarray),
+        strict_mode_(strict_mode) {}
+
+ private:
+  class FromBits:       public BitField<ElementsKind,    0, 8> {};
+  class ToBits:         public BitField<ElementsKind,    8, 8> {};
+  class IsJSArrayBits:  public BitField<bool,           16, 8> {};
+  class StrictModeBits: public BitField<StrictModeFlag, 24, 8> {};
+
+  Major MajorKey() { return FastElementsConversion; }
+  int MinorKey() {
+    return FromBits::encode(from_) |
+        ToBits::encode(to_) |
+        IsJSArrayBits::encode(is_jsarray_) |
+        StrictModeBits::encode(strict_mode_);
+  }
+
+  void Generate(MacroAssembler* masm);
+  static void GenerateSmiOnlyToObject(MacroAssembler* masm);
+  static void GenerateSmiOnlyToDouble(MacroAssembler* masm,
+                                      StrictModeFlag strict_mode);
+  static void GenerateDoubleToObject(MacroAssembler* masm,
+                                     StrictModeFlag strict_mode);
+
+  ElementsKind from_;
+  ElementsKind to_;
+  bool is_jsarray_;
+  StrictModeFlag strict_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(FastElementsConversionStub);
+};
+
 } }  // namespace v8::internal
 
 #endif  // V8_CODE_STUBS_H_
index 1e886e2..0cc21c4 100644 (file)
@@ -34,6 +34,7 @@
 #include "isolate.h"
 #include "jsregexp.h"
 #include "regexp-macro-assembler.h"
+#include "stub-cache.h"
 
 namespace v8 {
 namespace internal {
@@ -6749,6 +6750,13 @@ struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
   { ebx, edx, ecx, EMIT_REMEMBERED_SET},
   // KeyedStoreStubCompiler::GenerateStoreFastElement.
   { edi, edx, ecx, EMIT_REMEMBERED_SET},
+  // FastElementConversionStub::GenerateSmiOnlyToObject
+  // and FastElementsConversionStub::GenerateSmiOnlyToDouble
+  // and FastElementsConversionStub::GenerateDoubleToObject
+  { edx, ebx, edi, EMIT_REMEMBERED_SET},
+  // FastElementConversionStub::GenerateDoubleToObject
+  { eax, edx, esi, EMIT_REMEMBERED_SET},
+  { edx, eax, edi, EMIT_REMEMBERED_SET},
   // Null termination.
   { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET}
 };
@@ -6992,6 +7000,255 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
 }
 
 
+void FastElementsConversionStub::GenerateSmiOnlyToObject(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- eax    : value
+  //  -- ebx    : target map
+  //  -- ecx    : key
+  //  -- edx    : receiver
+  //  -- esp[0] : return address
+  // -----------------------------------
+  // Set transitioned map.
+  __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+  __ RecordWriteField(edx,
+                      HeapObject::kMapOffset,
+                      ebx,
+                      edi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+}
+
+
+void FastElementsConversionStub::GenerateSmiOnlyToDouble(
+    MacroAssembler* masm, StrictModeFlag strict_mode) {
+  // ----------- S t a t e -------------
+  //  -- eax    : value
+  //  -- ebx    : target map
+  //  -- ecx    : key
+  //  -- edx    : receiver
+  //  -- esp[0] : return address
+  // -----------------------------------
+  Label loop, entry, convert_hole, gc_required;
+  __ push(eax);
+  __ push(ebx);
+
+  __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+  __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset));
+
+  // Allocate new FixedDoubleArray.
+  // edx: receiver
+  // edi: length of source FixedArray (smi-tagged)
+  __ lea(esi, Operand(edi, times_4, FixedDoubleArray::kHeaderSize));
+  __ AllocateInNewSpace(esi, eax, ebx, no_reg, &gc_required, TAG_OBJECT);
+
+  // eax: destination FixedDoubleArray
+  // edi: number of elements
+  // edx: receiver
+  __ mov(FieldOperand(eax, HeapObject::kMapOffset),
+         Immediate(masm->isolate()->factory()->fixed_double_array_map()));
+  __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi);
+  __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset));
+  // Replace receiver's backing store with newly created FixedDoubleArray.
+  __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
+  __ mov(ebx, eax);
+  __ RecordWriteField(edx,
+                      JSObject::kElementsOffset,
+                      ebx,
+                      edi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+
+  __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset));
+  // Convert and copy elements
+  // esi: source FixedArray
+  // edi: number of elements to convert/copy
+  ExternalReference canonical_the_hole_nan_reference =
+      ExternalReference::address_of_the_hole_nan();
+  XMMRegister the_hole_nan = xmm1;
+  if (CpuFeatures::IsSupported(SSE2)) {
+    CpuFeatures::Scope use_sse2(SSE2);
+    __ movdbl(the_hole_nan,
+              Operand::StaticVariable(canonical_the_hole_nan_reference));
+  }
+  __ jmp(&entry);
+  __ bind(&loop);
+  __ sub(edi, Immediate(Smi::FromInt(1)));
+  __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize));
+  // ebx: current element from source
+  // edi: index of current element
+  __ JumpIfNotSmi(ebx, &convert_hole);
+
+  // Normal smi, convert it to double and store.
+  __ SmiUntag(ebx);
+  if (CpuFeatures::IsSupported(SSE2)) {
+    CpuFeatures::Scope fscope(SSE2);
+    __ cvtsi2sd(xmm0, ebx);
+    __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
+              xmm0);
+  } else {
+    __ push(ebx);
+    __ fild_s(Operand(esp, 0));
+    __ pop(ebx);
+    __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
+  }
+  __ jmp(&entry);
+
+  // Found hole, store hole_nan_as_double instead.
+  __ bind(&convert_hole);
+  if (CpuFeatures::IsSupported(SSE2)) {
+    CpuFeatures::Scope use_sse2(SSE2);
+    __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
+              the_hole_nan);
+  } else {
+    __ fld_d(Operand::StaticVariable(canonical_the_hole_nan_reference));
+    __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
+  }
+
+  __ bind(&entry);
+  __ test(edi, edi);
+  __ j(not_zero, &loop);
+
+  Label done;
+  __ pop(ebx);
+  __ pop(eax);
+  // eax: value
+  // ebx: target map
+  // Set transitioned map.
+  __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+  __ RecordWriteField(edx,
+                      HeapObject::kMapOffset,
+                      ebx,
+                      edi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+  // Restore esi.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ jmp(&done, Label::kNear);
+
+  __ bind(&gc_required);
+  // Restore registers before jumping into runtime.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ pop(ebx);
+  __ pop(eax);
+  KeyedStoreIC::GenerateRuntimeSetProperty(masm, strict_mode);
+  __ bind(&done);
+}
+
+
+void FastElementsConversionStub::GenerateDoubleToObject(
+    MacroAssembler* masm, StrictModeFlag strict_mode) {
+  // ----------- S t a t e -------------
+  //  -- eax    : value
+  //  -- ebx    : target map
+  //  -- ecx    : key
+  //  -- edx    : receiver
+  //  -- esp[0] : return address
+  // -----------------------------------
+  Label loop, entry, convert_hole, gc_required;
+  __ push(eax);
+  __ push(edx);
+  __ push(ebx);
+
+  __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+  __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
+
+  // Allocate new FixedArray.
+  // ebx: length of source FixedDoubleArray (smi-tagged)
+  __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize));
+  __ AllocateInNewSpace(edi, eax, esi, no_reg, &gc_required, TAG_OBJECT);
+
+  // eax: destination FixedArray
+  // ebx: number of elements
+  __ mov(FieldOperand(eax, HeapObject::kMapOffset),
+         Immediate(masm->isolate()->factory()->fixed_array_map()));
+  __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx);
+  __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+
+  // Box doubles into heap numbers.
+  // edi: source FixedDoubleArray
+  // eax: destination FixedArray
+  __ jmp(&entry);
+  __ bind(&loop);
+  __ sub(ebx, Immediate(Smi::FromInt(1)));
+  // ebx: index of current element (smi-tagged)
+  uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
+  __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32));
+  __ j(equal, &convert_hole);
+
+  // Non-hole double, copy value into a heap number.
+  __ AllocateHeapNumber(edx, esi, no_reg, &gc_required);
+  // edx: new heap number
+  if (CpuFeatures::IsSupported(SSE2)) {
+    CpuFeatures::Scope fscope(SSE2);
+    __ movdbl(xmm0,
+              FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
+    __ movdbl(FieldOperand(edx, HeapNumber::kValueOffset), xmm0);
+  } else {
+    __ mov(esi, FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
+    __ mov(FieldOperand(edx, HeapNumber::kValueOffset), esi);
+    __ mov(esi, FieldOperand(edi, ebx, times_4, offset));
+    __ mov(FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize), esi);
+  }
+  __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx);
+  __ mov(esi, ebx);
+  __ RecordWriteArray(eax,
+                      edx,
+                      esi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+  __ jmp(&entry);
+
+  // Replace the-hole nan with the-hole pointer.
+  __ bind(&convert_hole);
+  __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize),
+         masm->isolate()->factory()->the_hole_value());
+
+  __ bind(&entry);
+  __ test(ebx, ebx);
+  __ j(not_zero, &loop);
+
+  __ pop(ebx);
+  __ pop(edx);
+  // ebx: target map
+  // edx: receiver
+  // Set transitioned map.
+  __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+  __ RecordWriteField(edx,
+                      HeapObject::kMapOffset,
+                      ebx,
+                      edi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+  // Replace receiver's backing store with newly created and filled FixedArray.
+  __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
+  __ RecordWriteField(edx,
+                      JSObject::kElementsOffset,
+                      eax,
+                      edi,
+                      kDontSaveFPRegs,
+                      EMIT_REMEMBERED_SET,
+                      OMIT_SMI_CHECK);
+
+  // Restore registers.
+  Label done;
+  __ pop(eax);
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ jmp(&done, Label::kNear);
+
+  __ bind(&gc_required);
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ pop(ebx);
+  __ pop(edx);
+  __ pop(eax);
+  KeyedStoreIC::GenerateRuntimeSetProperty(masm, strict_mode);
+  __ bind(&done);
+}
+
 #undef __
 
 } }  // namespace v8::internal
index d5056a9..09e9f0f 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1593,7 +1593,7 @@ void KeyedIC::GetReceiverMapsForStub(Code* stub, MapList* result) {
         RelocInfo* info = it.rinfo();
         Object* object = info->target_object();
         ASSERT(object->IsMap());
-        result->Add(Map::cast(object));
+        AddOneReceiverMapIfMissing(result, Map::cast(object));
       }
     }
   }
@@ -1781,12 +1781,6 @@ Map* GetTransitionedMap(Map* map, MapList* maps_list) {
 MaybeObject* KeyedStoreIC::ComputePolymorphicStub(
     MapList* receiver_maps,
     StrictModeFlag strict_mode) {
-  // TODO(yangguo): <remove>
-  Code* generic_stub = (strict_mode == kStrictMode)
-      ? isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic_Strict)
-      : isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic);
-  // </remove>
-
   // Collect MONOMORPHIC stubs for all target_receiver_maps.
   CodeList handler_ics(receiver_maps->length());
   MapList transitioned_maps(receiver_maps->length());
@@ -1795,15 +1789,11 @@ MaybeObject* KeyedStoreIC::ComputePolymorphicStub(
     MaybeObject* maybe_cached_stub = NULL;
     Map* transitioned_map = GetTransitionedMap(receiver_map, receiver_maps);
     if (transitioned_map != NULL) {
-      // TODO(yangguo): Enable this code!
-      // maybe_cached_stub = FastElementsConversionStub(
-      //     receiver_map->elements_kind(),  // original elements_kind
-      //     transitioned_map->elements_kind(),
-      //     receiver_map->instance_type() == JS_ARRAY_TYPE,  // is_js_array
-      //     strict_mode_).TryGetCode();
-      // TODO(yangguo): <remove>
-      maybe_cached_stub = generic_stub;
-      // </remove>
+      maybe_cached_stub = FastElementsConversionStub(
+          receiver_map->elements_kind(),  // original elements_kind
+          transitioned_map->elements_kind(),
+          receiver_map->instance_type() == JS_ARRAY_TYPE,  // is_js_array
+          strict_mode).TryGetCode();
     } else {
       maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
           receiver_map, strict_mode);
similarity index 50%
rename from test/mjsunit/element-kind.js
rename to test/mjsunit/elements-kind.js
index 46fd8f5..0ae2e69 100644 (file)
@@ -25,7 +25,8 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Flags: --allow-natives-syntax --smi-only-arrays
+// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc
+
 // Test element kind of objects.
 // Since --smi-only-arrays affects builtins, its default setting at compile
 // time sticks if built with snapshot.  If --smi-only-arrays is deactivated
@@ -33,7 +34,6 @@
 // in this test case.  Depending on whether smi-only arrays are actually
 // enabled, this test takes the appropriate code path to check smi-only arrays.
 
-
 support_smi_only_arrays = %HasFastSmiOnlyElements([]);
 
 if (support_smi_only_arrays) {
@@ -42,108 +42,113 @@ if (support_smi_only_arrays) {
   print("Tests do NOT include smi-only arrays.");
 }
 
-var element_kind = {
-  fast_smi_only_elements            :  0,
-  fast_elements                     :  1,
-  fast_double_elements              :  2,
-  dictionary_elements               :  3,
-  external_byte_elements            :  4,
-  external_unsigned_byte_elements   :  5,
-  external_short_elements           :  6,
-  external_unsigned_short_elements  :  7,
-  external_int_elements             :  8,
-  external_unsigned_int_elements    :  9,
-  external_float_elements           : 10,
-  external_double_elements          : 11,
-  external_pixel_elements           : 12
+var elements_kind = {
+  fast_smi_only            :  'fast smi only elements',
+  fast                     :  'fast elements',
+  fast_double              :  'fast double elements',
+  dictionary               :  'dictionary elements',
+  external_byte            :  'external byte elements',
+  external_unsigned_byte   :  'external unsigned byte elements',
+  external_short           :  'external short elements',
+  external_unsigned_short  :  'external unsigned short elements',
+  external_int             :  'external int elements',
+  external_unsigned_int    :  'external unsigned int elements',
+  external_float           :  'external float elements',
+  external_double          :  'external double elements',
+  external_pixel           :  'external pixel elements'
 }
 
-// We expect an object to only be of one element kind.
-function assertKind(expected, 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));
+function getKind(obj) {
+  if (%HasFastSmiOnlyElements(obj)) return elements_kind.fast_smi_only;
+  if (%HasFastElements(obj)) return elements_kind.fast;
+  if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
+  if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
+  // Every external kind is also an external array.
+  assertTrue(%HasExternalArrayElements(obj));
+  if (%HasExternalByteElements(obj)) {
+    return elements_kind.external_byte;
+  }
+  if (%HasExternalUnsignedByteElements(obj)) {
+    return elements_kind.external_unsigned_byte;
+  }
+  if (%HasExternalShortElements(obj)) {
+    return elements_kind.external_short;
+  }
+  if (%HasExternalUnsignedShortElements(obj)) {
+    return elements_kind.external_unsigned_short;
+  }
+  if (%HasExternalIntElements(obj)) {
+    return elements_kind.external_int;
+  }
+  if (%HasExternalUnsignedIntElements(obj)) {
+    return elements_kind.external_unsigned_int;
+  }
+  if (%HasExternalFloatElements(obj)) {
+    return elements_kind.external_float;
+  }
+  if (%HasExternalDoubleElements(obj)) {
+    return elements_kind.external_double;
+  }
+  if (%HasExternalPixelElements(obj)) {
+    return elements_kind.external_pixel;
   }
-  assertEquals(expected == element_kind.fast_double_elements,
-               %HasFastDoubleElements(obj));
-  assertEquals(expected == element_kind.dictionary_elements,
-               %HasDictionaryElements(obj));
-  assertEquals(expected == element_kind.external_byte_elements,
-               %HasExternalByteElements(obj));
-  assertEquals(expected == element_kind.external_unsigned_byte_elements,
-               %HasExternalUnsignedByteElements(obj));
-  assertEquals(expected == element_kind.external_short_elements,
-               %HasExternalShortElements(obj));
-  assertEquals(expected == element_kind.external_unsigned_short_elements,
-               %HasExternalUnsignedShortElements(obj));
-  assertEquals(expected == element_kind.external_int_elements,
-               %HasExternalIntElements(obj));
-  assertEquals(expected == element_kind.external_unsigned_int_elements,
-               %HasExternalUnsignedIntElements(obj));
-  assertEquals(expected == element_kind.external_float_elements,
-               %HasExternalFloatElements(obj));
-  assertEquals(expected == element_kind.external_double_elements,
-               %HasExternalDoubleElements(obj));
-  assertEquals(expected == element_kind.external_pixel_elements,
-               %HasExternalPixelElements(obj));
-  // every external kind is also an external array
-  assertEquals(expected >= element_kind.external_byte_elements,
-               %HasExternalArrayElements(obj));
+}
+
+function assertKind(expected, obj, name_opt) {
+  if (!support_smi_only_arrays &&
+      expected == elements_kind.fast_smi_only) {
+    expected = elements_kind.fast;
+  }
+  assertEquals(expected, getKind(obj), name_opt);
 }
 
 var me = {};
-assertKind(element_kind.fast_elements, me);
+assertKind(elements_kind.fast, me);
 me.dance = 0xD15C0;
 me.drink = 0xC0C0A;
-assertKind(element_kind.fast_elements, me);
+assertKind(elements_kind.fast, me);
 
 var too = [1,2,3];
-assertKind(element_kind.fast_smi_only_elements, too);
+assertKind(elements_kind.fast_smi_only, too);
 too.dance = 0xD15C0;
 too.drink = 0xC0C0A;
-assertKind(element_kind.fast_smi_only_elements, too);
+assertKind(elements_kind.fast_smi_only, 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);
+assertKind(elements_kind.fast_smi_only, you);
 for (var i = 0; i < 1337; i++) {
   var val = i;
   if (i == 1336) {
-    assertKind(element_kind.fast_smi_only_elements, you);
+    assertKind(elements_kind.fast_smi_only, you);
     val = new Object();
   }
   you[i] = val;
 }
-assertKind(element_kind.fast_elements, you);
+assertKind(elements_kind.fast, you);
 
-assertKind(element_kind.dictionary_elements, new Array(0xDECAF));
+assertKind(elements_kind.dictionary, new Array(0xDECAF));
 
 var fast_double_array = new Array(0xDECAF);
 for (var i = 0; i < 0xDECAF; i++) fast_double_array[i] = i / 2;
-assertKind(element_kind.fast_double_elements, fast_double_array);
+assertKind(elements_kind.fast_double, fast_double_array);
 
-assertKind(element_kind.external_byte_elements,           new Int8Array(9001));
-assertKind(element_kind.external_unsigned_byte_elements,  new Uint8Array(007));
-assertKind(element_kind.external_short_elements,          new Int16Array(666));
-assertKind(element_kind.external_unsigned_short_elements, new Uint16Array(42));
-assertKind(element_kind.external_int_elements,            new Int32Array(0xF));
-assertKind(element_kind.external_unsigned_int_elements,   new Uint32Array(23));
-assertKind(element_kind.external_float_elements,          new Float32Array(7));
-assertKind(element_kind.external_double_elements,         new Float64Array(0));
-assertKind(element_kind.external_pixel_elements,          new PixelArray(512));
+assertKind(elements_kind.external_byte,           new Int8Array(9001));
+assertKind(elements_kind.external_unsigned_byte,  new Uint8Array(007));
+assertKind(elements_kind.external_short,          new Int16Array(666));
+assertKind(elements_kind.external_unsigned_short, new Uint16Array(42));
+assertKind(elements_kind.external_int,            new Int32Array(0xF));
+assertKind(elements_kind.external_unsigned_int,   new Uint32Array(23));
+assertKind(elements_kind.external_float,          new Float32Array(7));
+assertKind(elements_kind.external_double,         new Float64Array(0));
+assertKind(elements_kind.external_pixel,          new PixelArray(512));
 
 // Crankshaft support for smi-only array elements.
 function monomorphic(array) {
   for (var i = 0; i < 3; i++) {
     array[i] = i + 10;
   }
-  assertKind(element_kind.fast_smi_only_elements, array);
+  assertKind(elements_kind.fast_smi_only, array);
   for (var i = 0; i < 3; i++) {
     var a = array[i];
     assertEquals(i + 10, a);
@@ -160,42 +165,46 @@ function polymorphic(array, expected_kind) {
   assertEquals(42, a);
 }
 var smis = [1, 2, 3];
-var strings = ["one", "two", "three"];
-var doubles = [0, 0, 0]; doubles[0] = 1.5; doubles[1] = 2.5; doubles[2] = 3.5;
+var strings = [0, 0, 0]; strings[0] = "one";
+var doubles = [0, 0, 0]; doubles[0] = 1.5;
 assertKind(support_smi_only_arrays
-               ? element_kind.fast_double_elements
-               : element_kind.fast_elements,
+               ? elements_kind.fast_double
+               : elements_kind.fast,
            doubles);
 for (var i = 0; i < 3; i++) {
-  polymorphic(smis, element_kind.fast_smi_only_elements);
-  polymorphic(strings, element_kind.fast_elements);
-  polymorphic(doubles, support_smi_only_arrays
-                           ? element_kind.fast_double_elements
-                           : element_kind.fast_elements);
+  polymorphic(smis, elements_kind.fast_smi_only);
+}
+for (var i = 0; i < 3; i++) {
+  polymorphic(strings, elements_kind.fast);
+}
+for (var i = 0; i < 3; i++) {
+  polymorphic(doubles, i == 0 && support_smi_only_arrays
+                           ? elements_kind.fast_double
+                           : elements_kind.fast);
 }
+
+/* Element transitions have not been implemented in crankshaft yet.
 %OptimizeFunctionOnNextCall(polymorphic);
-polymorphic(smis, element_kind.fast_smi_only_elements);
-polymorphic(strings, element_kind.fast_elements);
-polymorphic(doubles, support_smi_only_arrays
-    ? element_kind.fast_double_elements
-    : element_kind.fast_elements);
+polymorphic(smis, elements_kind.fast_smi_only);
+polymorphic(strings, elements_kind.fast);
+polymorphic(doubles, elements_kind.fast);
 
 // Crankshaft support for smi-only elements in dynamic array literals.
 function get(foo) { return foo; }  // Used to generate dynamic values.
 
 function crankshaft_test() {
   var a = [get(1), get(2), get(3)];
-  assertKind(element_kind.fast_smi_only_elements, a);
+  assertKind(elements_kind.fast_smi_only, a);
   var b = [get(1), get(2), get("three")];
-  assertKind(element_kind.fast_elements, b);
+  assertKind(elements_kind.fast, b);
   var c = [get(1), get(2), get(3.5)];
-  // The full code generator doesn't support conversion to fast_double_elements
+  // The full code generator doesn't support conversion to fast_double
   // yet. Crankshaft does, but only with --smi-only-arrays support.
   if ((%GetOptimizationStatus(crankshaft_test) & 1) &&
       support_smi_only_arrays) {
-    assertKind(element_kind.fast_double_elements, c);
+    assertKind(elements_kind.fast_double, c);
   } else {
-    assertKind(element_kind.fast_elements, c);
+    assertKind(elements_kind.fast, c);
   }
 }
 for (var i = 0; i < 3; i++) {
@@ -203,6 +212,7 @@ for (var i = 0; i < 3; i++) {
 }
 %OptimizeFunctionOnNextCall(crankshaft_test);
 crankshaft_test();
+*/
 
 // Elements_kind transitions for arrays.
 
@@ -216,14 +226,14 @@ if (support_smi_only_arrays) {
   var a = [1, 2, 3];
   var b = [1, 2, 3];
   assertTrue(%HaveSameMap(a, b));
-  assertKind(element_kind.fast_smi_only_elements, a);
+  assertKind(elements_kind.fast_smi_only, a);
   var c = [1, 2, 3];
   c["case2"] = true;
   var d = [1, 2, 3];
   d["case2"] = true;
   assertTrue(%HaveSameMap(c, d));
   assertFalse(%HaveSameMap(a, c));
-  assertKind(element_kind.fast_smi_only_elements, c);
+  assertKind(elements_kind.fast_smi_only, c);
   var e = [1, 2, 3];
   e["case3"] = true;
   var f = [1, 2, 3];
@@ -231,31 +241,34 @@ if (support_smi_only_arrays) {
   assertTrue(%HaveSameMap(e, f));
   assertFalse(%HaveSameMap(a, e));
   assertFalse(%HaveSameMap(c, e));
-  assertKind(element_kind.fast_smi_only_elements, e);
+  assertKind(elements_kind.fast_smi_only, e);
   // Case 1: SMI->DOUBLE, DOUBLE->OBJECT, SMI->OBJECT.
   a[0] = 1.5;
-  assertKind(element_kind.fast_double_elements, a);
+  assertKind(elements_kind.fast_double, a);
   a[0] = "foo";
-  assertKind(element_kind.fast_elements, a);
+  assertKind(elements_kind.fast, a);
   b[0] = "bar";
   assertTrue(%HaveSameMap(a, b));
   // Case 2: SMI->DOUBLE, SMI->OBJECT, DOUBLE->OBJECT.
   c[0] = 1.5;
-  assertKind(element_kind.fast_double_elements, c);
+  assertKind(elements_kind.fast_double, c);
   assertFalse(%HaveSameMap(c, d));
   d[0] = "foo";
-  assertKind(element_kind.fast_elements, d);
+  assertKind(elements_kind.fast, d);
   assertFalse(%HaveSameMap(c, d));
   c[0] = "bar";
   assertTrue(%HaveSameMap(c, d));
   // Case 3: SMI->OBJECT, SMI->DOUBLE, DOUBLE->OBJECT.
   e[0] = "foo";
-  assertKind(element_kind.fast_elements, e);
+  assertKind(elements_kind.fast, e);
   assertFalse(%HaveSameMap(e, f));
   f[0] = 1.5;
-  assertKind(element_kind.fast_double_elements, f);
+  assertKind(elements_kind.fast_double, f);
   assertFalse(%HaveSameMap(e, f));
   f[0] = "bar";
-  assertKind(element_kind.fast_elements, f);
+  assertKind(elements_kind.fast, f);
   assertTrue(%HaveSameMap(e, f));
 }
+
+// Throw away type information in the ICs for next stress run.
+gc();
diff --git a/test/mjsunit/elements-transition.js b/test/mjsunit/elements-transition.js
new file mode 100644 (file)
index 0000000..f198324
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax --smi-only-arrays
+
+support_smi_only_arrays = %HasFastSmiOnlyElements([]);
+
+if (support_smi_only_arrays) {
+  function test(test_double, test_object, set, length) {
+    // We apply the same operations to two identical arrays.  The first array
+    // triggers an IC miss, upon which the conversion stub is generated, but the
+    // actual conversion is done in runtime.  The second array, arriving at
+    // the previously patched IC, is then converted using the conversion stub.
+    var array_1 = new Array(length);
+    var array_2 = new Array(length);
+
+    assertTrue(%HasFastSmiOnlyElements(array_1));
+    assertTrue(%HasFastSmiOnlyElements(array_2));
+    for (var i = 0; i < length; i++) {
+      if (i == length - 8 && test_double) {
+        set(array_1, i, 0.5);
+        set(array_2, i, 0.5);
+        assertTrue(%HasFastDoubleElements(array_1));
+        assertTrue(%HasFastDoubleElements(array_2));
+      } else if (i == length - 5 && test_object) {
+        set(array_1, i, 'object');
+        set(array_2, i, 'object');
+        assertTrue(%HasFastElements(array_1));
+        assertTrue(%HasFastElements(array_2));
+      } else {
+        set(array_1, i, 2*i+1);
+        set(array_2, i, 2*i+1);
+      }
+    }
+
+    for (var i = 0; i < length; i++) {
+      if (i == length - 8 && test_double) {
+        assertEquals(0.5, array_1[i]);
+        assertEquals(0.5, array_2[i]);
+      } else if (i == length - 5 && test_object) {
+        assertEquals('object', array_1[i]);
+        assertEquals('object', array_2[i]);
+      } else {
+        assertEquals(2*i+1, array_1[i]);
+        assertEquals(2*i+1, array_2[i]);
+      }
+    }
+  }
+
+  test(false, false, function(a,i,v){ a[i] = v; }, 100);
+  test(true,  false, function(a,i,v){ a[i] = v; }, 100);
+  test(false, true,  function(a,i,v){ a[i] = v; }, 100);
+  test(true,  true,  function(a,i,v){ a[i] = v; }, 100);
+
+  test(false, false, function(a,i,v){ a[i] = v; }, 10000);
+  test(true,  false, function(a,i,v){ a[i] = v; }, 10000);
+  test(false, true,  function(a,i,v){ a[i] = v; }, 10000);
+  test(true,  true,  function(a,i,v){ a[i] = v; }, 10000);
+} else {
+  print("Test skipped because smi only arrays are not supported.");
+}
\ No newline at end of file