From fae807b3bb471e70b640745c9b69816eea7c8e50 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Thu, 13 Oct 2011 10:53:31 +0000 Subject: [PATCH] Elements kind conversion in generated code (ia32). 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 | 24 ++ src/code-stubs.h | 44 +++- src/ia32/code-stubs-ia32.cc | 257 +++++++++++++++++++++ src/ic.cc | 22 +- test/mjsunit/{element-kind.js => elements-kind.js} | 213 +++++++++-------- test/mjsunit/elements-transition.js | 85 +++++++ 6 files changed, 528 insertions(+), 117 deletions(-) rename test/mjsunit/{element-kind.js => elements-kind.js} (50%) create mode 100644 test/mjsunit/elements-transition.js diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 4bc2603..78585d8 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -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 diff --git a/src/code-stubs.h b/src/code-stubs.h index acfbd46..e9e600f 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -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 {}; + class ToBits: public BitField {}; + class IsJSArrayBits: public BitField {}; + class StrictModeBits: public BitField {}; + + 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_ diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 1e886e2..0cc21c4 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -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 diff --git a/src/ic.cc b/src/ic.cc index d5056a9..09e9f0f 100644 --- 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): - Code* generic_stub = (strict_mode == kStrictMode) - ? isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic_Strict) - : isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic); - // - // 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): - maybe_cached_stub = generic_stub; - // + 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); diff --git a/test/mjsunit/element-kind.js b/test/mjsunit/elements-kind.js similarity index 50% rename from test/mjsunit/element-kind.js rename to test/mjsunit/elements-kind.js index 46fd8f5..0ae2e69 100644 --- a/test/mjsunit/element-kind.js +++ b/test/mjsunit/elements-kind.js @@ -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 index 0000000..f198324 --- /dev/null +++ b/test/mjsunit/elements-transition.js @@ -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 -- 2.7.4