}
-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;
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;
virtual KeyedAccessStoreMode GetStoreMode() {
return STANDARD_STORE;
}
- bool IsArrayLength() { return is_array_length_; }
bool IsUninitialized() { return is_uninitialized_; }
TypeFeedbackId PropertyFeedbackId() { return reuse(id()); }
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) { }
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;
V(Compare) \
V(CompareIC) \
V(MathPow) \
- V(ArrayLength) \
V(StringLength) \
V(FunctionPrototype) \
V(StoreArrayLength) \
};
-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) { }
}
+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) {
}
+void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle<Map> map) {
+ AddInstruction(new(zone()) HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckMaps(object, map, zone()));
+}
+
+
void HOptimizedGraphBuilder::AddCheckMapsWithTransitions(HValue* object,
Handle<Map> map) {
AddInstruction(new(zone()) HCheckNonSmi(object));
}
-void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
+bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad(
Property* expr,
HValue* object,
SmallMapList* types,
Handle<String> 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<String> 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> map;
LookupResult lookup(isolate());
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
Handle<Map> 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<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
return new(zone()) HConstant(function, Representation::Tagged());
}
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
Handle<JSObject> holder(lookup.holder());
Handle<Map> 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);
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
Handle<JSObject> holder(lookup.holder());
Handle<Map> holder_map(holder->map());
- AddCheckMapsWithTransitions(object, map);
+ AddCheckMap(object, map);
AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*holder_map));
return new(zone()) HConstant(function, Representation::Tagged());
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()));
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> map, BailoutId id);
HValue* object,
SmallMapList* types,
Handle<String> name);
+ bool HandlePolymorphicArrayLengthLoad(Property* expr,
+ HValue* object,
+ SmallMapList* types,
+ Handle<String> name);
void HandlePolymorphicStoreNamedField(Assignment* expr,
HValue* object,
HValue* value,
Property* expr,
Handle<Map> map);
+ void AddCheckMap(HValue* object, Handle<Map> map);
+
void AddCheckMapsWithTransitions(HValue* object,
Handle<Map> map);
}
-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
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<Code> 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()) &&
}
+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,
// 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;
}
if (!holder->HasFastProperties()) break;
return isolate()->stub_cache()->ComputeLoadViaGetter(
name, receiver, holder, Handle<JSFunction>::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;
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(
Handle<String> name,
Handle<Code> code);
void CopyICToMegamorphicCache(Handle<String> name);
+ bool IsTransitionedMapOfMonomorphicTarget(Map* receiver_map);
void PatchCache(State state,
StrictModeFlag strict_mode,
Handle<JSObject> receiver,
}
-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;
}
-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;