From 07def3cb1e56a4043e7e39a739ea577e738072c2 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Fri, 8 Jul 2011 10:46:10 +0000 Subject: [PATCH] Unify handling of element IC stubs. In the process, add shared stubs for DictionaryValue lookups that are handled in the same way as fast elements and external array elements. Includes code for MIPS, which compiles and run polymorph-arrays.js successfully. R=jkummerow@chromium.org BUG=none TEST=test/mjsunit/polymorph-arrays.js Review URL: http://codereview.chromium.org/7227010 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8579 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/ic-arm.cc | 99 +---------------- src/arm/macro-assembler-arm.cc | 94 ++++++++++++++++ src/arm/macro-assembler-arm.h | 10 ++ src/arm/stub-cache-arm.cc | 55 +++++++++- src/code-stubs.cc | 66 ++++++++--- src/code-stubs.h | 65 ++++------- src/ia32/ic-ia32.cc | 118 ++------------------ src/ia32/macro-assembler-ia32.cc | 98 +++++++++++++++++ src/ia32/macro-assembler-ia32.h | 9 ++ src/ia32/stub-cache-ia32.cc | 69 +++++++++++- src/ic.cc | 41 +++---- src/ic.h | 21 ++-- src/mips/ic-mips.cc | 113 +------------------ src/mips/macro-assembler-mips.cc | 108 ++++++++++++++++++ src/mips/macro-assembler-mips.h | 10 ++ src/mips/stub-cache-mips.cc | 56 +++++++++- src/stub-cache.cc | 35 +----- src/stub-cache.h | 8 +- src/x64/ic-x64.cc | 108 +----------------- src/x64/macro-assembler-x64.cc | 103 +++++++++++++++++ src/x64/macro-assembler-x64.h | 9 ++ src/x64/stub-cache-x64.cc | 53 ++++++++- test/mjsunit/polymorph-arrays.js | 182 +++++++++++++++++++++++++++++++ 23 files changed, 967 insertions(+), 563 deletions(-) create mode 100644 test/mjsunit/polymorph-arrays.js diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index dea875bad4..6038153a1a 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -212,101 +212,6 @@ static void GenerateDictionaryStore(MacroAssembler* masm, } -static void GenerateNumberDictionaryLoad(MacroAssembler* masm, - Label* miss, - Register elements, - Register key, - Register result, - Register t0, - Register t1, - Register t2) { - // Register use: - // - // elements - holds the slow-case elements of the receiver on entry. - // Unchanged unless 'result' is the same register. - // - // key - holds the smi key on entry. - // Unchanged unless 'result' is the same register. - // - // result - holds the result on exit if the load succeeded. - // Allowed to be the same as 'key' or 'result'. - // Unchanged on bailout so 'key' or 'result' can be used - // in further computation. - // - // Scratch registers: - // - // t0 - holds the untagged key on entry and holds the hash once computed. - // - // t1 - used to hold the capacity mask of the dictionary - // - // t2 - used for the index into the dictionary. - Label done; - - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - __ mvn(t1, Operand(t0)); - __ add(t0, t1, Operand(t0, LSL, 15)); - // hash = hash ^ (hash >> 12); - __ eor(t0, t0, Operand(t0, LSR, 12)); - // hash = hash + (hash << 2); - __ add(t0, t0, Operand(t0, LSL, 2)); - // hash = hash ^ (hash >> 4); - __ eor(t0, t0, Operand(t0, LSR, 4)); - // hash = hash * 2057; - __ mov(t1, Operand(2057)); - __ mul(t0, t0, t1); - // hash = hash ^ (hash >> 16); - __ eor(t0, t0, Operand(t0, LSR, 16)); - - // Compute the capacity mask. - __ ldr(t1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); - __ mov(t1, Operand(t1, ASR, kSmiTagSize)); // convert smi to int - __ sub(t1, t1, Operand(1)); - - // Generate an unrolled loop that performs a few probes before giving up. - static const int kProbes = 4; - for (int i = 0; i < kProbes; i++) { - // Use t2 for index calculations and keep the hash intact in t0. - __ mov(t2, t0); - // Compute the masked index: (hash + i + i * i) & mask. - if (i > 0) { - __ add(t2, t2, Operand(NumberDictionary::GetProbeOffset(i))); - } - __ and_(t2, t2, Operand(t1)); - - // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); - __ add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3 - - // Check if the key is identical to the name. - __ add(t2, elements, Operand(t2, LSL, kPointerSizeLog2)); - __ ldr(ip, FieldMemOperand(t2, NumberDictionary::kElementsStartOffset)); - __ cmp(key, Operand(ip)); - if (i != kProbes - 1) { - __ b(eq, &done); - } else { - __ b(ne, miss); - } - } - - __ bind(&done); - // Check that the value is a normal property. - // t2: elements + (index * kPointerSize) - const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; - __ ldr(t1, FieldMemOperand(t2, kDetailsOffset)); - __ tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); - __ b(ne, miss); - - // Get the value at the masked, scaled index and return. - const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; - __ ldr(result, FieldMemOperand(t2, kValueOffset)); -} - - void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name @@ -738,7 +643,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { __ b(ne, &slow_load); __ mov(r0, Operand(r2, ASR, kSmiTagSize)); // r0: untagged index - GenerateNumberDictionaryLoad(masm, &slow_load, r4, r2, r1, r0, r3, r5); + __ LoadFromNumberDictionary(&slow_load, r4, r2, r1, r0, r3, r5); __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, r0, r3); __ jmp(&do_call); @@ -1127,7 +1032,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ cmp(r3, ip); __ b(ne, &slow); __ mov(r2, Operand(r0, ASR, kSmiTagSize)); - GenerateNumberDictionaryLoad(masm, &slow, r4, r0, r0, r2, r3, r5); + __ LoadFromNumberDictionary(&slow, r4, r0, r0, r2, r3, r5); __ Ret(); // Slow case, key and receiver still in r0 and r1. diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 08a1cb9453..320879a627 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -1343,6 +1343,100 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register result, + Register t0, + Register t1, + Register t2) { + // Register use: + // + // elements - holds the slow-case elements of the receiver on entry. + // Unchanged unless 'result' is the same register. + // + // key - holds the smi key on entry. + // Unchanged unless 'result' is the same register. + // + // result - holds the result on exit if the load succeeded. + // Allowed to be the same as 'key' or 'result'. + // Unchanged on bailout so 'key' or 'result' can be used + // in further computation. + // + // Scratch registers: + // + // t0 - holds the untagged key on entry and holds the hash once computed. + // + // t1 - used to hold the capacity mask of the dictionary + // + // t2 - used for the index into the dictionary. + Label done; + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + mvn(t1, Operand(t0)); + add(t0, t1, Operand(t0, LSL, 15)); + // hash = hash ^ (hash >> 12); + eor(t0, t0, Operand(t0, LSR, 12)); + // hash = hash + (hash << 2); + add(t0, t0, Operand(t0, LSL, 2)); + // hash = hash ^ (hash >> 4); + eor(t0, t0, Operand(t0, LSR, 4)); + // hash = hash * 2057; + mov(t1, Operand(2057)); + mul(t0, t0, t1); + // hash = hash ^ (hash >> 16); + eor(t0, t0, Operand(t0, LSR, 16)); + + // Compute the capacity mask. + ldr(t1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + mov(t1, Operand(t1, ASR, kSmiTagSize)); // convert smi to int + sub(t1, t1, Operand(1)); + + // Generate an unrolled loop that performs a few probes before giving up. + static const int kProbes = 4; + for (int i = 0; i < kProbes; i++) { + // Use t2 for index calculations and keep the hash intact in t0. + mov(t2, t0); + // Compute the masked index: (hash + i + i * i) & mask. + if (i > 0) { + add(t2, t2, Operand(NumberDictionary::GetProbeOffset(i))); + } + and_(t2, t2, Operand(t1)); + + // Scale the index by multiplying by the element size. + ASSERT(NumberDictionary::kEntrySize == 3); + add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3 + + // Check if the key is identical to the name. + add(t2, elements, Operand(t2, LSL, kPointerSizeLog2)); + ldr(ip, FieldMemOperand(t2, NumberDictionary::kElementsStartOffset)); + cmp(key, Operand(ip)); + if (i != kProbes - 1) { + b(eq, &done); + } else { + b(ne, miss); + } + } + + bind(&done); + // Check that the value is a normal property. + // t2: elements + (index * kPointerSize) + const int kDetailsOffset = + NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + ldr(t1, FieldMemOperand(t2, kDetailsOffset)); + tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); + b(ne, miss); + + // Get the value at the masked, scaled index and return. + const int kValueOffset = + NumberDictionary::kElementsStartOffset + kPointerSize; + ldr(result, FieldMemOperand(t2, kValueOffset)); +} + + void MacroAssembler::AllocateInNewSpace(int object_size, Register result, Register scratch1, diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 1918858ebe..07281a7caf 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -433,6 +433,16 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + + void LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register result, + Register t0, + Register t1, + Register t2); + + inline void MarkCode(NopMarkerTypes type) { nop(type); } diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index caa6a0eef9..86e49716d3 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -3100,7 +3100,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- r1 : receiver // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedLoadElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(r1, r2, @@ -3193,7 +3194,10 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- r3 : scratch // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedStoreElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + MaybeObject* maybe_stub = + KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(r2, r3, @@ -3388,6 +3392,53 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { #define __ ACCESS_MASM(masm) +void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( + MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- lr : return address + // -- r0 : key + // -- r1 : receiver + // ----------------------------------- + Label slow, miss_force_generic; + + Register key = r0; + Register receiver = r1; + + __ JumpIfNotSmi(key, &miss_force_generic); + __ mov(r2, Operand(key, ASR, kSmiTagSize)); + __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ LoadFromNumberDictionary(&slow, r4, key, r0, r2, r3, r5); + __ Ret(); + + __ bind(&slow); + __ IncrementCounter( + masm->isolate()->counters()->keyed_load_external_array_slow(), + 1, r2, r3); + + // ---------- S t a t e -------------- + // -- lr : return address + // -- r0 : key + // -- r1 : receiver + // ----------------------------------- + Handle slow_ic = + masm->isolate()->builtins()->KeyedLoadIC_Slow(); + __ Jump(slow_ic, RelocInfo::CODE_TARGET); + + // Miss case, call the runtime. + __ bind(&miss_force_generic); + + // ---------- S t a t e -------------- + // -- lr : return address + // -- r0 : key + // -- r1 : receiver + // ----------------------------------- + + Handle miss_ic = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(miss_ic, RelocInfo::CODE_TARGET); +} + + static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { switch (elements_kind) { case JSObject::EXTERNAL_BYTE_ELEMENTS: diff --git a/src/code-stubs.cc b/src/code-stubs.cc index f56d833d40..4d6fd03fbb 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -244,23 +244,61 @@ const char* InstanceofStub::GetName() { } -void KeyedLoadFastElementStub::Generate(MacroAssembler* masm) { - KeyedLoadStubCompiler::GenerateLoadFastElement(masm); +void KeyedLoadElementStub::Generate(MacroAssembler* masm) { + switch (elements_kind_) { + case JSObject::FAST_ELEMENTS: + KeyedLoadStubCompiler::GenerateLoadFastElement(masm); + break; + case JSObject::FAST_DOUBLE_ELEMENTS: + UNIMPLEMENTED(); + break; + case JSObject::EXTERNAL_BYTE_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case JSObject::EXTERNAL_SHORT_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case JSObject::EXTERNAL_INT_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case JSObject::EXTERNAL_PIXEL_ELEMENTS: + KeyedLoadStubCompiler::GenerateLoadExternalArray(masm, elements_kind_); + break; + case JSObject::DICTIONARY_ELEMENTS: + KeyedLoadStubCompiler::GenerateLoadDictionaryElement(masm); + break; + case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + UNREACHABLE(); + break; + } } -void KeyedStoreFastElementStub::Generate(MacroAssembler* masm) { - KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_); -} - - -void KeyedLoadExternalArrayStub::Generate(MacroAssembler* masm) { - KeyedLoadStubCompiler::GenerateLoadExternalArray(masm, elements_kind_); -} - - -void KeyedStoreExternalArrayStub::Generate(MacroAssembler* masm) { - KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, elements_kind_); +void KeyedStoreElementStub::Generate(MacroAssembler* masm) { + switch (elements_kind_) { + case JSObject::FAST_ELEMENTS: + KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_); + break; + case JSObject::FAST_DOUBLE_ELEMENTS: + UNIMPLEMENTED(); + break; + case JSObject::EXTERNAL_BYTE_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case JSObject::EXTERNAL_SHORT_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case JSObject::EXTERNAL_INT_ELEMENTS: + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case JSObject::EXTERNAL_PIXEL_ELEMENTS: + KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, elements_kind_); + break; + case JSObject::DICTIONARY_ELEMENTS: + KeyedStoreStubCompiler::GenerateStoreDictionaryElement(masm); + break; + case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + UNREACHABLE(); + break; + } } diff --git a/src/code-stubs.h b/src/code-stubs.h index 5a6aa1ba03..1670ddf4d0 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -70,10 +70,8 @@ namespace internal { V(NumberToString) \ V(CEntry) \ V(JSEntry) \ - V(KeyedLoadFastElement) \ - V(KeyedStoreFastElement) \ - V(KeyedLoadExternalArray) \ - V(KeyedStoreExternalArray) \ + V(KeyedLoadElement) \ + V(KeyedStoreElement) \ V(DebuggerStatement) \ V(StringDictionaryNegativeLookup) @@ -892,60 +890,43 @@ class AllowStubCallsScope { }; -class KeyedLoadFastElementStub : public CodeStub { +class KeyedLoadElementStub : public CodeStub { public: - explicit KeyedLoadFastElementStub() { - } + explicit KeyedLoadElementStub(JSObject::ElementsKind elements_kind) + : elements_kind_(elements_kind) + { } - Major MajorKey() { return KeyedLoadFastElement; } - int MinorKey() { return 0; } + Major MajorKey() { return KeyedLoadElement; } + int MinorKey() { return elements_kind_; } void Generate(MacroAssembler* masm); + + private: + JSObject::ElementsKind elements_kind_; + + DISALLOW_COPY_AND_ASSIGN(KeyedLoadElementStub); }; -class KeyedStoreFastElementStub : public CodeStub { +class KeyedStoreElementStub : public CodeStub { public: - explicit KeyedStoreFastElementStub(bool is_js_array) - : is_js_array_(is_js_array) { } + KeyedStoreElementStub(bool is_js_array, + JSObject::ElementsKind elements_kind) + : is_js_array_(is_js_array), + elements_kind_(elements_kind) { } - Major MajorKey() { return KeyedStoreFastElement; } - int MinorKey() { return is_js_array_ ? 1 : 0; } + Major MajorKey() { return KeyedStoreElement; } + int MinorKey() { + return (is_js_array_ ? 0 : JSObject::kElementsKindCount) + elements_kind_; + } void Generate(MacroAssembler* masm); private: bool is_js_array_; -}; - - -class KeyedLoadExternalArrayStub : public CodeStub { - public: - explicit KeyedLoadExternalArrayStub(JSObject::ElementsKind elements_kind) - : elements_kind_(elements_kind) { } - - Major MajorKey() { return KeyedLoadExternalArray; } - int MinorKey() { return elements_kind_; } - - void Generate(MacroAssembler* masm); - - protected: JSObject::ElementsKind elements_kind_; -}; - -class KeyedStoreExternalArrayStub : public CodeStub { - public: - explicit KeyedStoreExternalArrayStub(JSObject::ElementsKind elements_kind) - : elements_kind_(elements_kind) { } - - Major MajorKey() { return KeyedStoreExternalArray; } - int MinorKey() { return elements_kind_; } - - void Generate(MacroAssembler* masm); - - protected: - JSObject::ElementsKind elements_kind_; + DISALLOW_COPY_AND_ASSIGN(KeyedStoreElementStub); }; diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index be5910a124..5f143b104f 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -216,105 +216,6 @@ static void GenerateDictionaryStore(MacroAssembler* masm, } -static void GenerateNumberDictionaryLoad(MacroAssembler* masm, - Label* miss, - Register elements, - Register key, - Register r0, - Register r1, - Register r2, - Register result) { - // Register use: - // - // elements - holds the slow-case elements of the receiver and is unchanged. - // - // key - holds the smi key on entry and is unchanged. - // - // Scratch registers: - // - // r0 - holds the untagged key on entry and holds the hash once computed. - // - // r1 - used to hold the capacity mask of the dictionary - // - // r2 - used for the index into the dictionary. - // - // result - holds the result on exit if the load succeeds and we fall through. - - Label done; - - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - __ mov(r1, r0); - __ not_(r0); - __ shl(r1, 15); - __ add(r0, Operand(r1)); - // hash = hash ^ (hash >> 12); - __ mov(r1, r0); - __ shr(r1, 12); - __ xor_(r0, Operand(r1)); - // hash = hash + (hash << 2); - __ lea(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - __ mov(r1, r0); - __ shr(r1, 4); - __ xor_(r0, Operand(r1)); - // hash = hash * 2057; - __ imul(r0, r0, 2057); - // hash = hash ^ (hash >> 16); - __ mov(r1, r0); - __ shr(r1, 16); - __ xor_(r0, Operand(r1)); - - // Compute capacity mask. - __ mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset)); - __ shr(r1, kSmiTagSize); // convert smi to int - __ dec(r1); - - // Generate an unrolled loop that performs a few probes before giving up. - const int kProbes = 4; - for (int i = 0; i < kProbes; i++) { - // Use r2 for index calculations and keep the hash intact in r0. - __ mov(r2, r0); - // Compute the masked index: (hash + i + i * i) & mask. - if (i > 0) { - __ add(Operand(r2), Immediate(NumberDictionary::GetProbeOffset(i))); - } - __ and_(r2, Operand(r1)); - - // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); - __ lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 - - // Check if the key matches. - __ cmp(key, FieldOperand(elements, - r2, - times_pointer_size, - NumberDictionary::kElementsStartOffset)); - if (i != (kProbes - 1)) { - __ j(equal, &done); - } else { - __ j(not_equal, miss); - } - } - - __ bind(&done); - // Check that the value is a normal propety. - const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; - ASSERT_EQ(NORMAL, 0); - __ test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), - Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); - __ j(not_zero, miss); - - // Get the value at the masked, scaled index. - const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; - __ mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); -} - - void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : receiver @@ -591,14 +492,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Push receiver on the stack to free up a register for the dictionary // probing. __ push(edx); - GenerateNumberDictionaryLoad(masm, - &slow_pop_receiver, - ecx, - eax, - ebx, - edx, - edi, - eax); + __ LoadFromNumberDictionary(&slow_pop_receiver, + ecx, + eax, + ebx, + edx, + edi, + eax); // Pop receiver before returning. __ pop(edx); __ ret(0); @@ -1200,8 +1100,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { __ SmiUntag(ebx); // ebx: untagged index // Receiver in edx will be clobbered, need to reload it on miss. - GenerateNumberDictionaryLoad( - masm, &slow_reload_receiver, eax, ecx, ebx, edx, edi, edi); + __ LoadFromNumberDictionary( + &slow_reload_receiver, eax, ecx, ebx, edx, edi, edi); __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1); __ jmp(&do_call); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 020acded7d..136b24c981 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -734,6 +734,104 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register r0, + Register r1, + Register r2, + Register result) { + // Register use: + // + // elements - holds the slow-case elements of the receiver and is unchanged. + // + // key - holds the smi key on entry and is unchanged. + // + // Scratch registers: + // + // r0 - holds the untagged key on entry and holds the hash once computed. + // + // r1 - used to hold the capacity mask of the dictionary + // + // r2 - used for the index into the dictionary. + // + // result - holds the result on exit if the load succeeds and we fall through. + + Label done; + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + mov(r1, r0); + not_(r0); + shl(r1, 15); + add(r0, Operand(r1)); + // hash = hash ^ (hash >> 12); + mov(r1, r0); + shr(r1, 12); + xor_(r0, Operand(r1)); + // hash = hash + (hash << 2); + lea(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + mov(r1, r0); + shr(r1, 4); + xor_(r0, Operand(r1)); + // hash = hash * 2057; + imul(r0, r0, 2057); + // hash = hash ^ (hash >> 16); + mov(r1, r0); + shr(r1, 16); + xor_(r0, Operand(r1)); + + // Compute capacity mask. + mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset)); + shr(r1, kSmiTagSize); // convert smi to int + dec(r1); + + // Generate an unrolled loop that performs a few probes before giving up. + const int kProbes = 4; + for (int i = 0; i < kProbes; i++) { + // Use r2 for index calculations and keep the hash intact in r0. + mov(r2, r0); + // Compute the masked index: (hash + i + i * i) & mask. + if (i > 0) { + add(Operand(r2), Immediate(NumberDictionary::GetProbeOffset(i))); + } + and_(r2, Operand(r1)); + + // Scale the index by multiplying by the entry size. + ASSERT(NumberDictionary::kEntrySize == 3); + lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 + + // Check if the key matches. + cmp(key, FieldOperand(elements, + r2, + times_pointer_size, + NumberDictionary::kElementsStartOffset)); + if (i != (kProbes - 1)) { + j(equal, &done); + } else { + j(not_equal, miss); + } + } + + bind(&done); + // Check that the value is a normal propety. + const int kDetailsOffset = + NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + ASSERT_EQ(NORMAL, 0); + test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), + Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); + j(not_zero, miss); + + // Get the value at the masked, scaled index. + const int kValueOffset = + NumberDictionary::kElementsStartOffset + kPointerSize; + mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); +} + + void MacroAssembler::LoadAllocationTopHelper(Register result, Register scratch, AllocationFlags flags) { diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 837c500e9a..dac22731a9 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -352,6 +352,15 @@ class MacroAssembler: public Assembler { Label* miss); + void LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register r0, + Register r1, + Register r2, + Register result); + + // --------------------------------------------------------------------------- // Allocation support diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e53cc0839b..2660850889 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -2679,7 +2679,10 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- esp[0] : return address // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedStoreElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE; + MaybeObject* maybe_stub = + KeyedStoreElementStub(is_jsarray, elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(edx, Handle(receiver_map), @@ -3137,7 +3140,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- esp[0] : return address // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedLoadElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(edx, Handle(receiver_map), @@ -3321,6 +3325,64 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { #define __ ACCESS_MASM(masm) +void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + Label slow, miss_force_generic; + + // This stub is meant to be tail-jumped to, the receiver must already + // have been verified by the caller to not be a smi. + __ JumpIfNotSmi(eax, &miss_force_generic); + __ mov(ebx, eax); + __ SmiUntag(ebx); + __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); + + // Push receiver on the stack to free up a register for the dictionary + // probing. + __ push(edx); + __ LoadFromNumberDictionary(&slow, + ecx, + eax, + ebx, + edx, + edi, + eax); + // Pop receiver before returning. + __ pop(edx); + __ ret(0); + + __ bind(&slow); + __ pop(edx); + + // ----------- S t a t e ------------- + // -- eax : value + // -- ecx : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + + Handle slow_ic = + masm->isolate()->builtins()->KeyedLoadIC_Slow(); + __ jmp(slow_ic, RelocInfo::CODE_TARGET); + + __ bind(&miss_force_generic); + // ----------- S t a t e ------------- + // -- eax : value + // -- ecx : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + + Handle miss_force_generic_ic = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ jmp(miss_force_generic_ic, RelocInfo::CODE_TARGET); +} + + void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, JSObject::ElementsKind elements_kind) { @@ -3731,7 +3793,8 @@ void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) { void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, bool is_js_array) { // ----------- S t a t e ------------- - // -- eax : key + // -- eax : value + // -- ecx : key // -- edx : receiver // -- esp[0] : return address // ----------------------------------- diff --git a/src/ic.cc b/src/ic.cc index eb0f12a394..f70f75a7f6 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1097,15 +1097,10 @@ void LoadIC::UpdateCaches(LookupResult* lookup, } -MaybeObject* KeyedLoadIC::GetFastElementStubWithoutMapCheck( - bool is_js_array) { - return KeyedLoadFastElementStub().TryGetCode(); -} - - -MaybeObject* KeyedLoadIC::GetExternalArrayStubWithoutMapCheck( +MaybeObject* KeyedLoadIC::GetElementStubWithoutMapCheck( + bool is_js_array, JSObject::ElementsKind elements_kind) { - return KeyedLoadExternalArrayStub(elements_kind).TryGetCode(); + return KeyedLoadElementStub(elements_kind).TryGetCode(); } @@ -1675,7 +1670,7 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver, for (int i = 0; i < target_receiver_maps.length(); ++i) { Map* receiver_map(target_receiver_maps.at(i)); MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck( - receiver_map, strict_mode, generic_stub); + receiver_map, strict_mode); Code* cached_stub; if (!maybe_cached_stub->To(&cached_stub)) return maybe_cached_stub; handler_ics.Add(cached_stub); @@ -1694,18 +1689,18 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver, MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck( Map* receiver_map, - StrictModeFlag strict_mode, - Code* generic_stub) { + StrictModeFlag strict_mode) { if ((receiver_map->instance_type() & kNotStringTag) == 0) { ASSERT(string_stub() != NULL); return string_stub(); - } else if (receiver_map->has_external_array_elements()) { - return GetExternalArrayStubWithoutMapCheck(receiver_map->elements_kind()); - } else if (receiver_map->has_fast_elements()) { - bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - return GetFastElementStubWithoutMapCheck(is_js_array); } else { - return generic_stub; + ASSERT(receiver_map->has_dictionary_elements() || + receiver_map->has_fast_elements() || + receiver_map->has_fast_double_elements() || + receiver_map->has_external_array_elements()); + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + return GetElementStubWithoutMapCheck(is_js_array, + receiver_map->elements_kind()); } } @@ -1717,6 +1712,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver, Code* result = NULL; if (receiver->HasFastElements() || receiver->HasExternalArrayElements() || + receiver->HasFastDoubleElements() || receiver->HasDictionaryElements()) { MaybeObject* maybe_stub = isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement( @@ -1729,15 +1725,10 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver, } -MaybeObject* KeyedStoreIC::GetFastElementStubWithoutMapCheck( - bool is_js_array) { - return KeyedStoreFastElementStub(is_js_array).TryGetCode(); -} - - -MaybeObject* KeyedStoreIC::GetExternalArrayStubWithoutMapCheck( +MaybeObject* KeyedStoreIC::GetElementStubWithoutMapCheck( + bool is_js_array, JSObject::ElementsKind elements_kind) { - return KeyedStoreExternalArrayStub(elements_kind).TryGetCode(); + return KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); } diff --git a/src/ic.h b/src/ic.h index 9a663ba6aa..11c2e3af45 100644 --- a/src/ic.h +++ b/src/ic.h @@ -345,10 +345,8 @@ class KeyedIC: public IC { explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {} virtual ~KeyedIC() {} - virtual MaybeObject* GetFastElementStubWithoutMapCheck( - bool is_js_array) = 0; - - virtual MaybeObject* GetExternalArrayStubWithoutMapCheck( + virtual MaybeObject* GetElementStubWithoutMapCheck( + bool is_js_array, JSObject::ElementsKind elements_kind) = 0; protected: @@ -373,8 +371,7 @@ class KeyedIC: public IC { MaybeObject* ComputeMonomorphicStubWithoutMapCheck( Map* receiver_map, - StrictModeFlag strict_mode, - Code* generic_stub); + StrictModeFlag strict_mode); MaybeObject* ComputeMonomorphicStub(JSObject* receiver, bool is_store, @@ -415,10 +412,8 @@ class KeyedLoadIC: public KeyedIC { static const int kSlowCaseBitFieldMask = (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor); - virtual MaybeObject* GetFastElementStubWithoutMapCheck( - bool is_js_array); - - virtual MaybeObject* GetExternalArrayStubWithoutMapCheck( + virtual MaybeObject* GetElementStubWithoutMapCheck( + bool is_js_array, JSObject::ElementsKind elements_kind); protected: @@ -568,10 +563,8 @@ class KeyedStoreIC: public KeyedIC { static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode); static void GenerateNonStrictArguments(MacroAssembler* masm); - virtual MaybeObject* GetFastElementStubWithoutMapCheck( - bool is_js_array); - - virtual MaybeObject* GetExternalArrayStubWithoutMapCheck( + virtual MaybeObject* GetElementStubWithoutMapCheck( + bool is_js_array, JSObject::ElementsKind elements_kind); protected: diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc index cbae8e46e6..81374e887c 100644 --- a/src/mips/ic-mips.cc +++ b/src/mips/ic-mips.cc @@ -214,115 +214,6 @@ static void GenerateDictionaryStore(MacroAssembler* masm, } -static void GenerateNumberDictionaryLoad(MacroAssembler* masm, - Label* miss, - Register elements, - Register key, - Register result, - Register reg0, - Register reg1, - Register reg2) { - // Register use: - // - // elements - holds the slow-case elements of the receiver on entry. - // Unchanged unless 'result' is the same register. - // - // key - holds the smi key on entry. - // Unchanged unless 'result' is the same register. - // - // - // result - holds the result on exit if the load succeeded. - // Allowed to be the same as 'key' or 'result'. - // Unchanged on bailout so 'key' or 'result' can be used - // in further computation. - // - // Scratch registers: - // - // reg0 - holds the untagged key on entry and holds the hash once computed. - // - // reg1 - Used to hold the capacity mask of the dictionary. - // - // reg2 - Used for the index into the dictionary. - // at - Temporary (avoid MacroAssembler instructions also using 'at'). - Label done; - - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - __ nor(reg1, reg0, zero_reg); - __ sll(at, reg0, 15); - __ addu(reg0, reg1, at); - - // hash = hash ^ (hash >> 12); - __ srl(at, reg0, 12); - __ xor_(reg0, reg0, at); - - // hash = hash + (hash << 2); - __ sll(at, reg0, 2); - __ addu(reg0, reg0, at); - - // hash = hash ^ (hash >> 4); - __ srl(at, reg0, 4); - __ xor_(reg0, reg0, at); - - // hash = hash * 2057; - __ li(reg1, Operand(2057)); - __ mul(reg0, reg0, reg1); - - // hash = hash ^ (hash >> 16); - __ srl(at, reg0, 16); - __ xor_(reg0, reg0, at); - - // Compute the capacity mask. - __ lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); - __ sra(reg1, reg1, kSmiTagSize); - __ Subu(reg1, reg1, Operand(1)); - - // Generate an unrolled loop that performs a few probes before giving up. - static const int kProbes = 4; - for (int i = 0; i < kProbes; i++) { - // Use reg2 for index calculations and keep the hash intact in reg0. - __ mov(reg2, reg0); - // Compute the masked index: (hash + i + i * i) & mask. - if (i > 0) { - __ Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i))); - } - __ and_(reg2, reg2, reg1); - - // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); - __ sll(at, reg2, 1); // 2x. - __ addu(reg2, reg2, at); // reg2 = reg2 * 3. - - // Check if the key is identical to the name. - __ sll(at, reg2, kPointerSizeLog2); - __ addu(reg2, elements, at); - - __ lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset)); - if (i != kProbes - 1) { - __ Branch(&done, eq, key, Operand(at)); - } else { - __ Branch(miss, ne, key, Operand(at)); - } - } - - __ bind(&done); - // Check that the value is a normal property. - // reg2: elements + (index * kPointerSize). - const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; - __ lw(reg1, FieldMemOperand(reg2, kDetailsOffset)); - __ And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); - __ Branch(miss, ne, at, Operand(zero_reg)); - - // Get the value at the masked, scaled index and return. - const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; - __ lw(result, FieldMemOperand(reg2, kValueOffset)); -} - - void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- a2 : name @@ -751,7 +642,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { __ Branch(&slow_load, ne, a3, Operand(at)); __ sra(a0, a2, kSmiTagSize); // a0: untagged index - GenerateNumberDictionaryLoad(masm, &slow_load, t0, a2, a1, a0, a3, t1); + __ LoadFromNumberDictionary(&slow_load, t0, a2, a1, a0, a3, t1); __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, a0, a3); __ jmp(&do_call); @@ -1136,7 +1027,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ LoadRoot(at, Heap::kHashTableMapRootIndex); __ Branch(&slow, ne, a3, Operand(at)); __ sra(a2, a0, kSmiTagSize); - GenerateNumberDictionaryLoad(masm, &slow, t0, a0, v0, a2, a3, t1); + __ LoadFromNumberDictionary(&slow, t0, a0, v0, a2, a3, t1); __ Ret(); // Slow case, key and receiver still in a0 and a1. diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index a8573d624e..ebd2114c23 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -424,6 +424,114 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register result, + Register reg0, + Register reg1, + Register reg2) { + // Register use: + // + // elements - holds the slow-case elements of the receiver on entry. + // Unchanged unless 'result' is the same register. + // + // key - holds the smi key on entry. + // Unchanged unless 'result' is the same register. + // + // + // result - holds the result on exit if the load succeeded. + // Allowed to be the same as 'key' or 'result'. + // Unchanged on bailout so 'key' or 'result' can be used + // in further computation. + // + // Scratch registers: + // + // reg0 - holds the untagged key on entry and holds the hash once computed. + // + // reg1 - Used to hold the capacity mask of the dictionary. + // + // reg2 - Used for the index into the dictionary. + // at - Temporary (avoid MacroAssembler instructions also using 'at'). + Label done; + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + nor(reg1, reg0, zero_reg); + sll(at, reg0, 15); + addu(reg0, reg1, at); + + // hash = hash ^ (hash >> 12); + srl(at, reg0, 12); + xor_(reg0, reg0, at); + + // hash = hash + (hash << 2); + sll(at, reg0, 2); + addu(reg0, reg0, at); + + // hash = hash ^ (hash >> 4); + srl(at, reg0, 4); + xor_(reg0, reg0, at); + + // hash = hash * 2057; + li(reg1, Operand(2057)); + mul(reg0, reg0, reg1); + + // hash = hash ^ (hash >> 16); + srl(at, reg0, 16); + xor_(reg0, reg0, at); + + // Compute the capacity mask. + lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + sra(reg1, reg1, kSmiTagSize); + Subu(reg1, reg1, Operand(1)); + + // Generate an unrolled loop that performs a few probes before giving up. + static const int kProbes = 4; + for (int i = 0; i < kProbes; i++) { + // Use reg2 for index calculations and keep the hash intact in reg0. + mov(reg2, reg0); + // Compute the masked index: (hash + i + i * i) & mask. + if (i > 0) { + Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i))); + } + and_(reg2, reg2, reg1); + + // Scale the index by multiplying by the element size. + ASSERT(NumberDictionary::kEntrySize == 3); + sll(at, reg2, 1); // 2x. + addu(reg2, reg2, at); // reg2 = reg2 * 3. + + // Check if the key is identical to the name. + sll(at, reg2, kPointerSizeLog2); + addu(reg2, elements, at); + + lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset)); + if (i != kProbes - 1) { + Branch(&done, eq, key, Operand(at)); + } else { + Branch(miss, ne, key, Operand(at)); + } + } + + bind(&done); + // Check that the value is a normal property. + // reg2: elements + (index * kPointerSize). + const int kDetailsOffset = + NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + lw(reg1, FieldMemOperand(reg2, kDetailsOffset)); + And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); + Branch(miss, ne, at, Operand(zero_reg)); + + // Get the value at the masked, scaled index and return. + const int kValueOffset = + NumberDictionary::kElementsStartOffset + kPointerSize; + lw(result, FieldMemOperand(reg2, kValueOffset)); +} + + // --------------------------------------------------------------------------- // Instruction macros. diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index 985ef0c830..23c5eb83b5 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -299,6 +299,16 @@ DECLARE_NOTARGET_PROTOTYPE(Ret) Register scratch, Label* miss); + + void LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register result, + Register reg0, + Register reg1, + Register reg2); + + inline void MarkCode(NopMarkerTypes type) { nop(type); } diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc index 40c31fc0a1..919bdc40c2 100644 --- a/src/mips/stub-cache-mips.cc +++ b/src/mips/stub-cache-mips.cc @@ -3099,7 +3099,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- a1 : receiver // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedLoadElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(a1, a2, @@ -3190,7 +3191,10 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- a3 : scratch // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedStoreElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + MaybeObject* maybe_stub = + KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(a2, a3, @@ -3390,6 +3394,54 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { #define __ ACCESS_MASM(masm) +void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( + MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- ra : return address + // -- a0 : key + // -- a1 : receiver + // ----------------------------------- + Label slow, miss_force_generic; + + Register key = a0; + Register receiver = a1; + + __ JumpIfNotSmi(key, &miss_force_generic); + __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ sra(a2, a0, kSmiTagSize); + __ LoadFromNumberDictionary(&slow, t0, a0, v0, a2, a3, t1); + __ Ret(); + + // Slow case, key and receiver still in a0 and a1. + __ bind(&slow); + __ IncrementCounter( + masm->isolate()->counters()->keyed_load_external_array_slow(), + 1, a2, a3); + // Entry registers are intact. + // ---------- S t a t e -------------- + // -- ra : return address + // -- a0 : key + // -- a1 : receiver + // ----------------------------------- + Handle slow_ic = + masm->isolate()->builtins()->KeyedLoadIC_Slow(); + __ Jump(slow_ic, RelocInfo::CODE_TARGET); + + // Miss case, call the runtime. + __ bind(&miss_force_generic); + + // ---------- S t a t e -------------- + // -- ra : return address + // -- a0 : key + // -- a1 : receiver + // ----------------------------------- + + Handle miss_ic = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(miss_ic, RelocInfo::CODE_TARGET); +} + + static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { switch (elements_kind) { case JSObject::EXTERNAL_BYTE_ELEMENTS: diff --git a/src/stub-cache.cc b/src/stub-cache.cc index eb813814d4..79cd7a0d22 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -1686,23 +1686,6 @@ MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type, } -MaybeObject* KeyedLoadStubCompiler::ComputeSharedKeyedLoadElementStub( - Map* receiver_map) { - MaybeObject* maybe_stub = NULL; - if (receiver_map->has_fast_elements()) { - maybe_stub = KeyedLoadFastElementStub().TryGetCode(); - } else if (receiver_map->has_external_array_elements()) { - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); - maybe_stub = KeyedLoadExternalArrayStub(elements_kind).TryGetCode(); - } else if (receiver_map->has_dictionary_elements()) { - maybe_stub = isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Slow); - } else { - UNREACHABLE(); - } - return maybe_stub; -} - - MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode_); @@ -1739,21 +1722,9 @@ MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, } -MaybeObject* KeyedStoreStubCompiler::ComputeSharedKeyedStoreElementStub( - Map* receiver_map) { - MaybeObject* maybe_stub = NULL; - if (receiver_map->has_fast_elements()) { - bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - maybe_stub = KeyedStoreFastElementStub(is_js_array).TryGetCode(); - } else if (receiver_map->has_external_array_elements()) { - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); - maybe_stub = KeyedStoreExternalArrayStub(elements_kind).TryGetCode(); - } else if (receiver_map->has_dictionary_elements()) { - maybe_stub = isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Slow); - } else { - UNREACHABLE(); - } - return maybe_stub; +void KeyedStoreStubCompiler::GenerateStoreDictionaryElement( + MacroAssembler* masm) { + KeyedStoreIC::GenerateSlow(masm); } diff --git a/src/stub-cache.h b/src/stub-cache.h index fa2676061d..93c50fa988 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -662,12 +662,12 @@ class KeyedLoadStubCompiler: public StubCompiler { static void GenerateLoadFastElement(MacroAssembler* masm); + static void GenerateLoadDictionaryElement(MacroAssembler* masm); + private: MaybeObject* GetCode(PropertyType type, String* name, InlineCacheState state = MONOMORPHIC); - - MaybeObject* ComputeSharedKeyedLoadElementStub(Map* receiver_map); }; @@ -720,13 +720,13 @@ class KeyedStoreStubCompiler: public StubCompiler { static void GenerateStoreExternalArray(MacroAssembler* masm, JSObject::ElementsKind elements_kind); + static void GenerateStoreDictionaryElement(MacroAssembler* masm); + private: MaybeObject* GetCode(PropertyType type, String* name, InlineCacheState state = MONOMORPHIC); - MaybeObject* ComputeSharedKeyedStoreElementStub(Map* receiver_map); - StrictModeFlag strict_mode_; }; diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 342f672e64..339d2c19ce 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -225,110 +225,6 @@ static void GenerateDictionaryStore(MacroAssembler* masm, } -static void GenerateNumberDictionaryLoad(MacroAssembler* masm, - Label* miss, - Register elements, - Register key, - Register r0, - Register r1, - Register r2, - Register result) { - // Register use: - // - // elements - holds the slow-case elements of the receiver on entry. - // Unchanged unless 'result' is the same register. - // - // key - holds the smi key on entry. - // Unchanged unless 'result' is the same register. - // - // Scratch registers: - // - // r0 - holds the untagged key on entry and holds the hash once computed. - // - // r1 - used to hold the capacity mask of the dictionary - // - // r2 - used for the index into the dictionary. - // - // result - holds the result on exit if the load succeeded. - // Allowed to be the same as 'key' or 'result'. - // Unchanged on bailout so 'key' or 'result' can be used - // in further computation. - - Label done; - - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - __ movl(r1, r0); - __ notl(r0); - __ shll(r1, Immediate(15)); - __ addl(r0, r1); - // hash = hash ^ (hash >> 12); - __ movl(r1, r0); - __ shrl(r1, Immediate(12)); - __ xorl(r0, r1); - // hash = hash + (hash << 2); - __ leal(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - __ movl(r1, r0); - __ shrl(r1, Immediate(4)); - __ xorl(r0, r1); - // hash = hash * 2057; - __ imull(r0, r0, Immediate(2057)); - // hash = hash ^ (hash >> 16); - __ movl(r1, r0); - __ shrl(r1, Immediate(16)); - __ xorl(r0, r1); - - // Compute capacity mask. - __ SmiToInteger32(r1, - FieldOperand(elements, NumberDictionary::kCapacityOffset)); - __ decl(r1); - - // Generate an unrolled loop that performs a few probes before giving up. - const int kProbes = 4; - for (int i = 0; i < kProbes; i++) { - // Use r2 for index calculations and keep the hash intact in r0. - __ movq(r2, r0); - // Compute the masked index: (hash + i + i * i) & mask. - if (i > 0) { - __ addl(r2, Immediate(NumberDictionary::GetProbeOffset(i))); - } - __ and_(r2, r1); - - // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); - __ lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 - - // Check if the key matches. - __ cmpq(key, FieldOperand(elements, - r2, - times_pointer_size, - NumberDictionary::kElementsStartOffset)); - if (i != (kProbes - 1)) { - __ j(equal, &done); - } else { - __ j(not_equal, miss); - } - } - - __ bind(&done); - // Check that the value is a normal propety. - const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; - ASSERT_EQ(NORMAL, 0); - __ Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), - Smi::FromInt(PropertyDetails::TypeField::mask())); - __ j(not_zero, miss); - - // Get the value at the masked, scaled index. - const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; - __ movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); -} - - void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : receiver @@ -535,7 +431,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), Heap::kHashTableMapRootIndex); __ j(not_equal, &slow); - GenerateNumberDictionaryLoad(masm, &slow, rcx, rax, rbx, r9, rdi, rax); + __ LoadFromNumberDictionary(&slow, rcx, rax, rbx, r9, rdi, rax); __ ret(0); __ bind(&slow); @@ -1099,7 +995,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { __ j(not_equal, &slow_load); __ SmiToInteger32(rbx, rcx); // ebx: untagged index - GenerateNumberDictionaryLoad(masm, &slow_load, rax, rcx, rbx, r9, rdi, rdi); + __ LoadFromNumberDictionary(&slow_load, rax, rcx, rbx, r9, rdi, rdi); __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1); __ jmp(&do_call); diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index dbed6e0fda..1df0228434 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -3204,6 +3204,109 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register r0, + Register r1, + Register r2, + Register result) { + // Register use: + // + // elements - holds the slow-case elements of the receiver on entry. + // Unchanged unless 'result' is the same register. + // + // key - holds the smi key on entry. + // Unchanged unless 'result' is the same register. + // + // Scratch registers: + // + // r0 - holds the untagged key on entry and holds the hash once computed. + // + // r1 - used to hold the capacity mask of the dictionary + // + // r2 - used for the index into the dictionary. + // + // result - holds the result on exit if the load succeeded. + // Allowed to be the same as 'key' or 'result'. + // Unchanged on bailout so 'key' or 'result' can be used + // in further computation. + + Label done; + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + movl(r1, r0); + notl(r0); + shll(r1, Immediate(15)); + addl(r0, r1); + // hash = hash ^ (hash >> 12); + movl(r1, r0); + shrl(r1, Immediate(12)); + xorl(r0, r1); + // hash = hash + (hash << 2); + leal(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + movl(r1, r0); + shrl(r1, Immediate(4)); + xorl(r0, r1); + // hash = hash * 2057; + imull(r0, r0, Immediate(2057)); + // hash = hash ^ (hash >> 16); + movl(r1, r0); + shrl(r1, Immediate(16)); + xorl(r0, r1); + + // Compute capacity mask. + SmiToInteger32(r1, + FieldOperand(elements, NumberDictionary::kCapacityOffset)); + decl(r1); + + // Generate an unrolled loop that performs a few probes before giving up. + const int kProbes = 4; + for (int i = 0; i < kProbes; i++) { + // Use r2 for index calculations and keep the hash intact in r0. + movq(r2, r0); + // Compute the masked index: (hash + i + i * i) & mask. + if (i > 0) { + addl(r2, Immediate(NumberDictionary::GetProbeOffset(i))); + } + and_(r2, r1); + + // Scale the index by multiplying by the entry size. + ASSERT(NumberDictionary::kEntrySize == 3); + lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 + + // Check if the key matches. + cmpq(key, FieldOperand(elements, + r2, + times_pointer_size, + NumberDictionary::kElementsStartOffset)); + if (i != (kProbes - 1)) { + j(equal, &done); + } else { + j(not_equal, miss); + } + } + + bind(&done); + // Check that the value is a normal propety. + const int kDetailsOffset = + NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + ASSERT_EQ(NORMAL, 0); + Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), + Smi::FromInt(PropertyDetails::TypeField::mask())); + j(not_zero, miss); + + // Get the value at the masked, scaled index. + const int kValueOffset = + NumberDictionary::kElementsStartOffset + kPointerSize; + movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); +} + + void MacroAssembler::LoadAllocationTopHelper(Register result, Register scratch, AllocationFlags flags) { diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index f09fafc202..47ce01bd0c 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -846,6 +846,15 @@ class MacroAssembler: public Assembler { Label* miss); + void LoadFromNumberDictionary(Label* miss, + Register elements, + Register key, + Register r0, + Register r1, + Register r2, + Register result); + + // --------------------------------------------------------------------------- // Allocation support diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index da27fdf050..71ce856169 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -2538,7 +2538,10 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- rsp[0] : return address // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedStoreElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + MaybeObject* maybe_stub = + KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(rdx, Handle(receiver_map), @@ -2994,7 +2997,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- rsp[0] : return address // ----------------------------------- Code* stub; - MaybeObject* maybe_stub = ComputeSharedKeyedLoadElementStub(receiver_map); + JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(rdx, Handle(receiver_map), @@ -3177,6 +3181,51 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { #define __ ACCESS_MASM(masm) +void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label slow, miss_force_generic; + + // This stub is meant to be tail-jumped to, the receiver must already + // have been verified by the caller to not be a smi. + + __ JumpIfNotSmi(rax, &miss_force_generic); + __ SmiToInteger32(rbx, rax); + __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); + + // Check whether the elements is a number dictionary. + // rdx: receiver + // rax: key + // rbx: key as untagged int32 + // rcx: elements + __ LoadFromNumberDictionary(&slow, rcx, rax, rbx, r9, rdi, rax); + __ ret(0); + + __ bind(&slow); + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Handle slow_ic = + masm->isolate()->builtins()->KeyedLoadIC_Slow(); + __ jmp(slow_ic, RelocInfo::CODE_TARGET); + + __ bind(&miss_force_generic); + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Handle miss_ic = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ jmp(miss_ic, RelocInfo::CODE_TARGET); +} + void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, JSObject::ElementsKind elements_kind) { diff --git a/test/mjsunit/polymorph-arrays.js b/test/mjsunit/polymorph-arrays.js new file mode 100644 index 0000000000..76fd89853c --- /dev/null +++ b/test/mjsunit/polymorph-arrays.js @@ -0,0 +1,182 @@ +// 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 +function init_array(a) { + for (var i = 0; i < 10; ++i ){ + a[i] = i; + } +} + +function init_sparse_array(a) { + for (var i = 0; i < 10; ++i ){ + a[i] = i; + } + a[5000000] = 256; + assertTrue(%HasDictionaryElements(a)); +} + +function testPolymorphicLoads() { + function make_polymorphic_load_function() { + function load(a, i) { + return a[i]; + } + + var object_array = new Object; + var sparse_object_array = new Object; + var js_array = new Array(10); + var sparse_js_array = new Array(5000001); + + init_array(object_array); + init_array(js_array); + init_sparse_array(sparse_object_array); + init_sparse_array(sparse_js_array); + + return load; + } + + var object_array = new Object; + var sparse_object_array = new Object; + var js_array = new Array(10); + var sparse_js_array = new Array(5000001); + + init_array(object_array); + init_array(js_array); + init_sparse_array(sparse_object_array); + init_sparse_array(sparse_js_array); + + // load() should now use polymorphic element loads. + load = make_polymorphic_load_function(); + assertEquals(1, load(object_array, 1)); + load = make_polymorphic_load_function(); + assertEquals(1, load(js_array, 1)); + load = make_polymorphic_load_function(); + assertEquals(1, load(sparse_object_array, 1)); + load = make_polymorphic_load_function(); + assertEquals(1, load(sparse_js_array, 1)); + + load = make_polymorphic_load_function(); + assertEquals(undefined, load(js_array, new Object())); + load = make_polymorphic_load_function(); + assertEquals(undefined, load(object_array, new Object())); + load = make_polymorphic_load_function(); + assertEquals(undefined, load(sparse_js_array, new Object())); + load = make_polymorphic_load_function(); + assertEquals(undefined, load(sparse_object_array, new Object())); + + // Try with crankshaft. + load = make_polymorphic_load_function(); + %OptimizeFunctionOnNextCall(load); + assertEquals(1, load(object_array, 1)); + assertEquals(1, load(js_array, 1)); + assertEquals(1, load(sparse_object_array, 1)); + assertEquals(1, load(sparse_js_array, 1)); + + load = make_polymorphic_load_function(); + %OptimizeFunctionOnNextCall(load); + assertEquals(undefined, load(js_array, new Object())); + load = make_polymorphic_load_function(); + %OptimizeFunctionOnNextCall(load); + assertEquals(undefined, load(object_array, new Object())); + load = make_polymorphic_load_function(); + %OptimizeFunctionOnNextCall(load); + assertEquals(undefined, load(sparse_js_array, new Object())); + load = make_polymorphic_load_function(); + %OptimizeFunctionOnNextCall(load); + assertEquals(undefined, load(sparse_object_array, new Object())); +} + +function testPolymorphicStores() { + function make_polymorphic_store_function() { + function store(a, i, val) { + a[i] = val; + } + + var object_array = new Object; + var sparse_object_array = new Object; + var js_array = new Array(10); + var sparse_js_array = new Array(5000001); + + init_array(object_array); + init_array(js_array); + init_sparse_array(sparse_object_array); + init_sparse_array(sparse_js_array); + + store(object_array, 1, 256); + store(js_array, 1, 256); + store(sparse_object_array, 1, 256); + store(sparse_js_array, 1, 256); + + return store; + } + + var object_array = new Object; + var sparse_object_array = new Object; + var js_array = new Array(10); + var sparse_js_array = new Array(5000001); + + init_array(object_array); + init_array(js_array); + init_sparse_array(sparse_object_array); + init_sparse_array(sparse_js_array); + + store = make_polymorphic_store_function(); + store(object_array, 2, 257); + store = make_polymorphic_store_function(); + store(js_array, 2, 257); + store = make_polymorphic_store_function(); + store(sparse_object_array, 2, 257); + store = make_polymorphic_store_function(); + store(sparse_js_array, 2, 257); + + assertEquals(257, object_array[2]); + assertEquals(257, js_array[2]); + assertEquals(257, sparse_js_array[2]); + assertEquals(257, sparse_object_array[2]); + + // Now try Crankshaft optimized polymorphic stores + store = make_polymorphic_store_function(); + %OptimizeFunctionOnNextCall(store); + store(object_array, 3, 258); + store = make_polymorphic_store_function(); + %OptimizeFunctionOnNextCall(store); + store(js_array, 3, 258); + store = make_polymorphic_store_function(); + %OptimizeFunctionOnNextCall(store); + store(sparse_object_array, 3, 258); + store = make_polymorphic_store_function(); + %OptimizeFunctionOnNextCall(store); + store(sparse_js_array, 3, 258); + + assertEquals(258, object_array[3]); + assertEquals(258, js_array[3]); + assertEquals(258, sparse_js_array[3]); + assertEquals(258, sparse_object_array[3]); +} + +testPolymorphicLoads(); +testPolymorphicStores();