diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 1304b0e474..0fd0de7f2b 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -4488,35 +4488,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } -void ArrayLengthStub::Generate(MacroAssembler* masm) { - Label miss; - Register receiver; - if (kind() == Code::KEYED_LOAD_IC) { - // ----------- S t a t e ------------- - // -- lr : return address - // -- r0 : key - // -- r1 : receiver - // ----------------------------------- - __ cmp(r0, Operand(masm->isolate()->factory()->length_string())); - __ b(ne, &miss); - receiver = r1; - } else { - ASSERT(kind() == Code::LOAD_IC); - // ----------- S t a t e ------------- - // -- r2 : name - // -- lr : return address - // -- r0 : receiver - // -- sp[0] : receiver - // ----------------------------------- - receiver = r0; - } - - StubCompiler::GenerateLoadArrayLength(masm, receiver, r3, &miss); - __ bind(&miss); - StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind())); -} - - void FunctionPrototypeStub::Generate(MacroAssembler* masm) { Label miss; Register receiver; diff --git a/src/ast.cc b/src/ast.cc index a6984ddb28..1ed0176020 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -414,12 +414,9 @@ void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle, is_monomorphic_ = oracle->LoadIsMonomorphicNormal(this); receiver_types_.Clear(); if (key()->IsPropertyName()) { - ArrayLengthStub array_stub(Code::LOAD_IC); FunctionPrototypeStub proto_stub(Code::LOAD_IC); StringLengthStub string_stub(Code::LOAD_IC, false); - if (oracle->LoadIsStub(this, &array_stub)) { - is_array_length_ = true; - } else if (oracle->LoadIsStub(this, &string_stub)) { + if (oracle->LoadIsStub(this, &string_stub)) { is_string_length_ = true; } else if (oracle->LoadIsStub(this, &proto_stub)) { is_function_prototype_ = true; diff --git a/src/ast.h b/src/ast.h index 88cd69605d..725b4688b6 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1488,7 +1488,6 @@ class Property: public Expression { virtual KeyedAccessStoreMode GetStoreMode() { return STANDARD_STORE; } - bool IsArrayLength() { return is_array_length_; } bool IsUninitialized() { return is_uninitialized_; } TypeFeedbackId PropertyFeedbackId() { return reuse(id()); } @@ -1504,7 +1503,6 @@ class Property: public Expression { load_id_(GetNextId(isolate)), is_monomorphic_(false), is_uninitialized_(false), - is_array_length_(false), is_string_length_(false), is_string_access_(false), is_function_prototype_(false) { } @@ -1518,7 +1516,6 @@ class Property: public Expression { SmallMapList receiver_types_; bool is_monomorphic_ : 1; bool is_uninitialized_ : 1; - bool is_array_length_ : 1; bool is_string_length_ : 1; bool is_string_access_ : 1; bool is_function_prototype_ : 1; diff --git a/src/code-stubs.h b/src/code-stubs.h index 92a3450ca2..1ee7c89d2a 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -47,7 +47,6 @@ namespace internal { V(Compare) \ V(CompareIC) \ V(MathPow) \ - V(ArrayLength) \ V(StringLength) \ V(FunctionPrototype) \ V(StoreArrayLength) \ @@ -597,16 +596,6 @@ class ICStub: public PlatformCodeStub { }; -class ArrayLengthStub: public ICStub { - public: - explicit ArrayLengthStub(Code::Kind kind) : ICStub(kind) { } - virtual void Generate(MacroAssembler* masm); - - private: - virtual CodeStub::Major MajorKey() { return ArrayLength; } -}; - - class FunctionPrototypeStub: public ICStub { public: explicit FunctionPrototypeStub(Code::Kind kind) : ICStub(kind) { } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 435805fbfb..c2305d7bbf 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1235,6 +1235,13 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( } +HInstruction* HGraphBuilder::BuildFastArrayLengthLoad(HValue* object, + HValue* typecheck) { + Zone* zone = this->zone(); + return new (zone) HJSArrayLength(object, typecheck, HType::Smi()); +} + + HValue* HGraphBuilder::BuildAllocateElements(HValue* context, ElementsKind kind, HValue* capacity) { @@ -6287,6 +6294,12 @@ static int ComputeLoadStoreFieldIndex(Handle type, } +void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle map) { + AddInstruction(new(zone()) HCheckNonSmi(object)); + AddInstruction(new(zone()) HCheckMaps(object, map, zone())); +} + + void HOptimizedGraphBuilder::AddCheckMapsWithTransitions(HValue* object, Handle map) { AddInstruction(new(zone()) HCheckNonSmi(object)); @@ -6398,15 +6411,39 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic( } -void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( +bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad( Property* expr, HValue* object, SmallMapList* types, Handle name) { + if (!name->Equals(isolate()->heap()->length_string())) return false; + + for (int i = 0; i < types->length(); i++) { + if (types->at(i)->instance_type() != JS_ARRAY_TYPE) return false; + } + + AddInstruction(new(zone()) HCheckNonSmi(object)); + HInstruction* typecheck = + AddInstruction(HCheckInstanceType::NewIsJSArray(object, zone())); + HInstruction* instr = BuildFastArrayLengthLoad(object, typecheck); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); + return true; +} + + +void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, + HValue* object, + SmallMapList* types, + Handle name) { int count = 0; int previous_field_offset = 0; bool previous_field_is_in_object = false; bool is_monomorphic_field = true; + + if (HandlePolymorphicArrayLengthLoad(expr, object, types, name)) + return; + Handle map; LookupResult lookup(isolate()); for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { @@ -7042,16 +7079,25 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( Handle map) { // Handle a load from a known field. ASSERT(!map->is_dictionary_map()); + + // Handle access to various length properties + if (name->Equals(isolate()->heap()->length_string())) { + if (map->instance_type() == JS_ARRAY_TYPE) { + AddCheckMapsWithTransitions(object, map); + return BuildFastArrayLengthLoad(object, NULL); + } + } + LookupResult lookup(isolate()); map->LookupDescriptor(NULL, *name, &lookup); if (lookup.IsField()) { - AddCheckMapsWithTransitions(object, map); + AddCheckMap(object, map); return BuildLoadNamedField(object, map, &lookup); } // Handle a load of a constant known function. if (lookup.IsConstantFunction()) { - AddCheckMapsWithTransitions(object, map); + AddCheckMap(object, map); Handle function(lookup.GetConstantFunctionFromMap(*map)); return new(zone()) HConstant(function, Representation::Tagged()); } @@ -7062,7 +7108,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( Handle prototype(JSObject::cast(map->prototype())); Handle holder(lookup.holder()); Handle holder_map(holder->map()); - AddCheckMapsWithTransitions(object, map); + AddCheckMap(object, map); HInstruction* holder_value = AddInstruction( new(zone()) HCheckPrototypeMaps(prototype, holder, zone())); return BuildLoadNamedField(holder_value, holder_map, &lookup); @@ -7073,7 +7119,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( Handle prototype(JSObject::cast(map->prototype())); Handle holder(lookup.holder()); Handle holder_map(holder->map()); - AddCheckMapsWithTransitions(object, map); + AddCheckMap(object, map); AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder, zone())); Handle function(lookup.GetConstantFunctionFromMap(*holder_map)); return new(zone()) HConstant(function, Representation::Tagged()); @@ -7533,13 +7579,7 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) { CHECK_ALIVE(VisitForValue(expr->obj())); HInstruction* instr = NULL; - if (expr->AsProperty()->IsArrayLength()) { - HValue* array = Pop(); - AddInstruction(new(zone()) HCheckNonSmi(array)); - HInstruction* mapcheck = - AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone())); - instr = new(zone()) HJSArrayLength(array, mapcheck); - } else if (expr->IsStringLength()) { + if (expr->IsStringLength()) { HValue* string = Pop(); AddInstruction(new(zone()) HCheckNonSmi(string)); AddInstruction(HCheckInstanceType::NewIsString(string, zone())); diff --git a/src/hydrogen.h b/src/hydrogen.h index 6dea1a6807..30ec1411e6 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -939,6 +939,8 @@ class HGraphBuilder { KeyedAccessStoreMode store_mode, Representation checked_index_representation = Representation::None()); + HInstruction* BuildFastArrayLengthLoad(HValue* object, HValue* typecheck); + HInstruction* BuildStoreMap(HValue* object, HValue* map, BailoutId id); HInstruction* BuildStoreMap(HValue* object, Handle map, BailoutId id); @@ -1351,6 +1353,10 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor { HValue* object, SmallMapList* types, Handle name); + bool HandlePolymorphicArrayLengthLoad(Property* expr, + HValue* object, + SmallMapList* types, + Handle name); void HandlePolymorphicStoreNamedField(Assignment* expr, HValue* object, HValue* value, @@ -1425,6 +1431,8 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor { Property* expr, Handle map); + void AddCheckMap(HValue* object, Handle map); + void AddCheckMapsWithTransitions(HValue* object, Handle map); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index a23f44ad4e..7734f96f28 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3304,25 +3304,6 @@ void MathPowStub::Generate(MacroAssembler* masm) { } -void ArrayLengthStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- ecx : name - // -- edx : receiver - // -- esp[0] : return address - // ----------------------------------- - Label miss; - - if (kind() == Code::KEYED_LOAD_IC) { - __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string())); - __ j(not_equal, &miss); - } - - StubCompiler::GenerateLoadArrayLength(masm, edx, eax, &miss); - __ bind(&miss); - StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind())); -} - - void FunctionPrototypeStub::Generate(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- ecx : name diff --git a/src/ic.cc b/src/ic.cc index d85caaaa27..515c6f71a1 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -857,28 +857,6 @@ MaybeObject* LoadIC::Load(State state, return Smi::FromInt(String::cast(*string)->length()); } - // Use specialized code for getting the length of arrays. - if (object->IsJSArray() && - name->Equals(isolate()->heap()->length_string())) { - Handle stub; - if (state == UNINITIALIZED) { - stub = pre_monomorphic_stub(); - } else if (state == PREMONOMORPHIC) { - ArrayLengthStub array_length_stub(kind()); - stub = array_length_stub.GetCode(isolate()); - } else if (state != MEGAMORPHIC) { - ASSERT(state != GENERIC); - stub = megamorphic_stub(); - } - if (!stub.is_null()) { - set_target(*stub); -#ifdef DEBUG - if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n"); -#endif - } - return JSArray::cast(*object)->length(); - } - // Use specialized code for getting prototype of functions. if (object->IsJSFunction() && name->Equals(isolate()->heap()->prototype_string()) && @@ -1037,6 +1015,22 @@ void IC::CopyICToMegamorphicCache(Handle name) { } +bool IC::IsTransitionedMapOfMonomorphicTarget(Map* receiver_map) { + AssertNoAllocation no_allocation; + + Map* current_map = target()->FindFirstMap(); + ElementsKind receiver_elements_kind = receiver_map->elements_kind(); + bool more_general_transition = + IsMoreGeneralElementsKindTransition( + current_map->elements_kind(), receiver_elements_kind); + Map* transitioned_map = more_general_transition + ? current_map->LookupElementsTransitionMap(receiver_elements_kind) + : NULL; + + return transitioned_map == receiver_map; +} + + // Since GC may have been invoked, by the time PatchCache is called, |state| is // not necessarily equal to target()->state(). void IC::PatchCache(State state, @@ -1054,6 +1048,17 @@ void IC::PatchCache(State state, // Only move to megamorphic if the target changes. if (target() != *code) { if (target()->is_load_stub()) { + bool is_same_handler = false; + { + AssertNoAllocation no_allocation; + Code* old_handler = target()->FindFirstCode(); + is_same_handler = old_handler == *code; + } + if (is_same_handler + && IsTransitionedMapOfMonomorphicTarget(receiver->map())) { + UpdateMonomorphicIC(receiver, code, name); + break; + } if (UpdatePolymorphicIC(state, strict_mode, receiver, name, code)) { break; } @@ -1226,6 +1231,12 @@ Handle LoadIC::ComputeLoadHandler(LookupResult* lookup, if (!holder->HasFastProperties()) break; return isolate()->stub_cache()->ComputeLoadViaGetter( name, receiver, holder, Handle::cast(getter)); + } else if (receiver->IsJSArray() && + name->Equals(isolate()->heap()->length_string())) { + PropertyIndex lengthIndex = + PropertyIndex::NewHeaderIndex(JSArray::kLengthOffset / kPointerSize); + return isolate()->stub_cache()->ComputeLoadField( + name, receiver, holder, lengthIndex); } // TODO(dcarney): Handle correctly. if (callback->IsDeclaredAccessorInfo()) break; @@ -1705,16 +1716,7 @@ Handle KeyedStoreIC::StoreElementStub(Handle receiver, transitioned_receiver_map = ComputeTransitionedMap(receiver, store_mode); } - ElementsKind transitioned_kind = - transitioned_receiver_map->elements_kind(); - bool more_general_transition = - IsMoreGeneralElementsKindTransition( - previous_receiver_map->elements_kind(), - transitioned_kind); - Map* transitioned_previous_map = more_general_transition - ? previous_receiver_map->LookupElementsTransitionMap(transitioned_kind) - : NULL; - if (transitioned_previous_map == *transitioned_receiver_map) { + if (IsTransitionedMapOfMonomorphicTarget(*transitioned_receiver_map)) { // Element family is the same, use the "worst" case map. store_mode = GetNonTransitioningStoreMode(store_mode); return isolate()->stub_cache()->ComputeKeyedStoreElement( diff --git a/src/ic.h b/src/ic.h index 2b8f378298..55b5661929 100644 --- a/src/ic.h +++ b/src/ic.h @@ -176,6 +176,7 @@ class IC { Handle name, Handle code); void CopyICToMegamorphicCache(Handle name); + bool IsTransitionedMapOfMonomorphicTarget(Map* receiver_map); void PatchCache(State state, StrictModeFlag strict_mode, Handle receiver, diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index 70c04198a6..15a53284b0 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -4555,35 +4555,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } -void ArrayLengthStub::Generate(MacroAssembler* masm) { - Label miss; - Register receiver; - if (kind() == Code::KEYED_LOAD_IC) { - // ----------- S t a t e ------------- - // -- ra : return address - // -- a0 : key - // -- a1 : receiver - // ----------------------------------- - __ Branch(&miss, ne, a0, - Operand(masm->isolate()->factory()->length_string())); - receiver = a1; - } else { - ASSERT(kind() == Code::LOAD_IC); - // ----------- S t a t e ------------- - // -- a2 : name - // -- ra : return address - // -- a0 : receiver - // -- sp[0] : receiver - // ----------------------------------- - receiver = a0; - } - - StubCompiler::GenerateLoadArrayLength(masm, receiver, a3, &miss); - __ bind(&miss); - StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind())); -} - - void FunctionPrototypeStub::Generate(MacroAssembler* masm) { Label miss; Register receiver; diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 7ba3459427..43ae8ee595 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2386,34 +2386,6 @@ void MathPowStub::Generate(MacroAssembler* masm) { } -void ArrayLengthStub::Generate(MacroAssembler* masm) { - Label miss; - Register receiver; - if (kind() == Code::KEYED_LOAD_IC) { - // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - __ Cmp(rax, masm->isolate()->factory()->length_string()); - __ j(not_equal, &miss); - receiver = rdx; - } else { - ASSERT(kind() == Code::LOAD_IC); - // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address - // ----------------------------------- - receiver = rax; - } - - StubCompiler::GenerateLoadArrayLength(masm, receiver, r8, &miss); - __ bind(&miss); - StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind())); -} - - void FunctionPrototypeStub::Generate(MacroAssembler* masm) { Label miss; Register receiver;