Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 9401c4ec584096136368102864cf18806434e2d9..202f0dbfbcb5d9bb264f79f086b7cf671ff9dc29 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -4062,20 +4062,6 @@ static bool LookupAccessorPair(Handle<Map> map, |
} |
-static bool LookupGetter(Handle<Map> map, |
- Handle<String> name, |
- Handle<JSFunction>* getter, |
- Handle<JSObject>* holder) { |
- Handle<AccessorPair> accessors; |
- if (LookupAccessorPair(map, name, &accessors, holder) && |
- accessors->getter()->IsJSFunction()) { |
- *getter = Handle<JSFunction>(JSFunction::cast(accessors->getter())); |
- return true; |
- } |
- return false; |
-} |
- |
- |
static bool LookupSetter(Handle<Map> map, |
Handle<String> name, |
Handle<JSFunction>* setter, |
@@ -4543,140 +4529,179 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic( |
} |
-static bool CanLoadPropertyFromPrototype(Handle<Map> map, |
- Handle<Name> name, |
- LookupResult* lookup) { |
- if (!CanInlinePropertyAccess(*map)) return false; |
- map->LookupDescriptor(NULL, *name, lookup); |
- if (lookup->IsFound()) return false; |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad( |
+ PropertyAccessInfo* info) { |
+ if (!CanInlinePropertyAccess(*map_)) return false; |
+ |
+ if (!LookupDescriptor()) return false; |
+ |
+ if (!lookup_.IsFound()) { |
+ return (!info->lookup_.IsFound() || !info->holder_.is_null()) && |
+ map_->prototype() == info->map_->prototype(); |
+ } |
+ |
+ if (lookup_.IsPropertyCallbacks()) { |
+ return accessor_.is_identical_to(info->accessor_); |
+ } |
+ |
+ if (lookup_.IsConstant()) { |
+ return constant_.is_identical_to(info->constant_); |
+ } |
+ |
+ ASSERT(lookup_.IsField()); |
+ if (!info->lookup_.IsField()) return false; |
+ |
+ Representation r = access_.representation(); |
+ if (!info->access_.representation().IsCompatibleForLoad(r)) return false; |
+ if (info->access_.offset() != access_.offset()) return false; |
+ if (info->access_.IsInobject() != access_.IsInobject()) return false; |
+ info->GeneralizeRepresentation(r); |
return true; |
} |
-HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic( |
- HValue* object, |
- SmallMapList* types, |
- Handle<String> name) { |
- // Use monomorphic load if property lookup results in the same field index |
- // for all maps. Requires special map check on the set of all handled maps. |
- if (types->length() > kMaxLoadPolymorphism) return NULL; |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() { |
+ map_->LookupDescriptor(NULL, *name_, &lookup_); |
+ return LoadResult(map_); |
+} |
- LookupResult lookup(isolate()); |
- int count; |
- HObjectAccess access = HObjectAccess::ForMap(); // initial value unused. |
- for (count = 0; count < types->length(); ++count) { |
- Handle<Map> map = types->at(count); |
- if (!ComputeLoadStoreField(map, name, &lookup, false)) break; |
- HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name); |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) { |
+ if (lookup_.IsField()) { |
+ access_ = HObjectAccess::ForField(map, &lookup_, name_); |
+ } else if (lookup_.IsPropertyCallbacks()) { |
+ Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate()); |
+ if (!callback->IsAccessorPair()) return false; |
+ Object* getter = Handle<AccessorPair>::cast(callback)->getter(); |
+ if (!getter->IsJSFunction()) return false; |
+ accessor_ = handle(JSFunction::cast(getter)); |
+ } else if (lookup_.IsConstant()) { |
+ constant_ = handle(lookup_.GetConstantFromMap(*map), isolate()); |
+ } |
- if (count == 0) { |
- // First time through the loop; set access and representation. |
- access = new_access; |
- } else if (!access.representation().IsCompatibleForLoad( |
- new_access.representation())) { |
- // Representations did not match. |
- break; |
- } else if (access.offset() != new_access.offset()) { |
- // Offsets did not match. |
- break; |
- } else if (access.IsInobject() != new_access.IsInobject()) { |
- // In-objectness did not match. |
- break; |
+ return true; |
+} |
+ |
+ |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() { |
+ Handle<Map> map = map_; |
+ while (map->prototype()->IsJSObject()) { |
+ holder_ = handle(JSObject::cast(map->prototype())); |
+ map = Handle<Map>(holder_->map()); |
+ if (!CanInlinePropertyAccess(*map)) { |
+ lookup_.NotFound(); |
+ return false; |
} |
- access = access.WithRepresentation( |
- access.representation().generalize(new_access.representation())); |
+ map->LookupDescriptor(*holder_, *name_, &lookup_); |
+ if (lookup_.IsFound()) return LoadResult(map); |
} |
+ lookup_.NotFound(); |
+ return true; |
+} |
- if (count == types->length()) { |
- // Everything matched; can use monomorphic load. |
- BuildCheckHeapObject(object); |
- HCheckMaps* checked_object = Add<HCheckMaps>(object, types); |
- return BuildLoadNamedField(checked_object, access); |
- } |
- if (count != 0) return NULL; |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() { |
+ if (!CanInlinePropertyAccess(*map_)) return IsStringLength(); |
+ if (IsArrayLength()) return true; |
+ if (!LookupDescriptor()) return false; |
+ if (lookup_.IsFound()) return true; |
+ return LookupInPrototypes(); |
+} |
+ |
- // Second chance: the property is on the prototype and all maps have the |
- // same prototype. |
- Handle<Map> map(types->at(0)); |
- if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return NULL; |
+bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( |
+ SmallMapList* types) { |
+ ASSERT(map_.is_identical_to(types->first())); |
+ if (!CanLoadMonomorphic()) return false; |
+ if (types->length() > kMaxLoadPolymorphism) return false; |
- Handle<Object> prototype(map->prototype(), isolate()); |
- for (count = 1; count < types->length(); ++count) { |
- Handle<Map> test_map(types->at(count)); |
- if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return NULL; |
- if (test_map->prototype() != *prototype) return NULL; |
+ if (IsStringLength()) { |
+ for (int i = 1; i < types->length(); ++i) { |
+ if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false; |
+ } |
+ return true; |
} |
- LookupInPrototypes(map, name, &lookup); |
- if (!lookup.IsField()) return NULL; |
+ if (IsArrayLength()) { |
+ bool is_fast = IsFastElementsKind(map_->elements_kind()); |
+ for (int i = 1; i < types->length(); ++i) { |
+ Handle<Map> test_map = types->at(i); |
+ if (test_map->instance_type() != JS_ARRAY_TYPE) return false; |
+ if (IsFastElementsKind(test_map->elements_kind()) != is_fast) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
- BuildCheckHeapObject(object); |
- Add<HCheckMaps>(object, types); |
+ for (int i = 1; i < types->length(); ++i) { |
+ PropertyAccessInfo test_info(isolate(), types->at(i), name_); |
+ if (!test_info.IsCompatibleForLoad(this)) return false; |
+ } |
- Handle<JSObject> holder(lookup.holder()); |
- Handle<Map> holder_map(holder->map()); |
- HValue* checked_holder = BuildCheckPrototypeMaps( |
- Handle<JSObject>::cast(prototype), holder); |
- return BuildLoadNamedField(checked_holder, |
- HObjectAccess::ForField(holder_map, &lookup, name)); |
+ return true; |
} |
-// Returns true if an instance of this map can never find a property with this |
-// name in its prototype chain. This means all prototypes up to the top are |
-// fast and don't have the name in them. It would be good if we could optimize |
-// polymorphic loads where the property is sometimes found in the prototype |
-// chain. |
-static bool PrototypeChainCanNeverResolve( |
- Handle<Map> map, Handle<String> name) { |
- Isolate* isolate = map->GetIsolate(); |
- Object* current = map->prototype(); |
- while (current != isolate->heap()->null_value()) { |
- if (current->IsJSGlobalProxy() || |
- current->IsGlobalObject() || |
- !current->IsJSObject() || |
- !CanInlinePropertyAccess(JSObject::cast(current)->map()) || |
- JSObject::cast(current)->IsAccessCheckNeeded()) { |
- return false; |
- } |
+HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic( |
+ PropertyAccessInfo* info, |
+ HValue* object, |
+ HInstruction* checked_object, |
+ BailoutId ast_id, |
+ BailoutId return_id, |
+ bool can_inline_accessor) { |
+ if (info->IsStringLength()) { |
+ return New<HLoadNamedField>( |
+ checked_object, HObjectAccess::ForStringLength()); |
+ } |
- LookupResult lookup(isolate); |
- Map* map = JSObject::cast(current)->map(); |
- map->LookupDescriptor(NULL, *name, &lookup); |
- if (lookup.IsFound()) return false; |
- if (!lookup.IsCacheable()) return false; |
- current = JSObject::cast(current)->GetPrototype(); |
+ if (info->IsArrayLength()) { |
+ return New<HLoadNamedField>( |
+ checked_object, HObjectAccess::ForArrayLength( |
+ info->map()->elements_kind())); |
} |
- return true; |
+ |
+ HValue* checked_holder = checked_object; |
+ if (info->has_holder()) { |
+ Handle<JSObject> prototype(JSObject::cast(info->map()->prototype())); |
+ checked_holder = BuildCheckPrototypeMaps(prototype, info->holder()); |
+ } |
+ |
+ if (!info->lookup()->IsFound()) return graph()->GetConstantUndefined(); |
+ |
+ if (info->lookup()->IsField()) { |
+ return BuildLoadNamedField(checked_holder, info->access()); |
+ } |
+ |
+ if (info->lookup()->IsPropertyCallbacks()) { |
+ Push(checked_object); |
+ if (FLAG_inline_accessors && |
+ can_inline_accessor && |
+ TryInlineGetter(info->accessor(), ast_id, return_id)) { |
+ return NULL; |
+ } |
+ Add<HPushArgument>(Pop()); |
+ return new(zone()) HCallConstantFunction(info->accessor(), 1); |
+ } |
+ |
+ ASSERT(info->lookup()->IsConstant()); |
+ return New<HConstant>(info->constant()); |
} |
void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( |
int position, |
BailoutId ast_id, |
+ BailoutId return_id, |
HValue* object, |
SmallMapList* types, |
Handle<String> name) { |
- HInstruction* instr = TryLoadPolymorphicAsMonomorphic(object, types, name); |
- if (instr != NULL) { |
- instr->set_position(position); |
- return ast_context()->ReturnInstruction(instr, ast_id); |
- } |
- |
// Something did not match; must use a polymorphic load. |
int count = 0; |
HBasicBlock* join = NULL; |
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { |
- Handle<Map> map = types->at(i); |
- LookupResult lookup(isolate()); |
- if (ComputeLoadStoreField(map, name, &lookup, false) || |
- (lookup.IsCacheable() && |
- CanInlinePropertyAccess(*map) && |
- (lookup.IsConstant() || |
- (!lookup.IsFound() && |
- PrototypeChainCanNeverResolve(map, name))))) { |
+ PropertyAccessInfo info(isolate(), types->at(i), name); |
+ if (info.CanLoadMonomorphic()) { |
if (count == 0) { |
BuildCheckHeapObject(object); |
join = graph()->CreateBasicBlock(); |
@@ -4684,37 +4709,25 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( |
++count; |
HBasicBlock* if_true = graph()->CreateBasicBlock(); |
HBasicBlock* if_false = graph()->CreateBasicBlock(); |
- HCompareMap* compare = |
- new(zone()) HCompareMap(object, map, if_true, if_false); |
+ HCompareMap* compare = new(zone()) HCompareMap( |
+ object, info.map(), if_true, if_false); |
current_block()->Finish(compare); |
set_current_block(if_true); |
- // TODO(verwaest): Merge logic with BuildLoadNamedMonomorphic. |
- if (lookup.IsField()) { |
- HObjectAccess access = HObjectAccess::ForField(map, &lookup, name); |
- HLoadNamedField* load = BuildLoadNamedField(compare, access); |
- load->set_position(position); |
- AddInstruction(load); |
- if (!ast_context()->IsEffect()) Push(load); |
- } else if (lookup.IsConstant()) { |
- Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate()); |
- HConstant* hconstant = Add<HConstant>(constant); |
- if (!ast_context()->IsEffect()) Push(hconstant); |
+ HInstruction* load = BuildLoadMonomorphic( |
+ &info, object, compare, ast_id, return_id, FLAG_polymorphic_inlining); |
+ if (load == NULL) { |
+ if (HasStackOverflow()) return; |
} else { |
- ASSERT(!lookup.IsFound()); |
- if (map->prototype()->IsJSObject()) { |
- Handle<JSObject> prototype(JSObject::cast(map->prototype())); |
- Handle<JSObject> holder = prototype; |
- while (holder->map()->prototype()->IsJSObject()) { |
- holder = handle(JSObject::cast(holder->map()->prototype())); |
- } |
- BuildCheckPrototypeMaps(prototype, holder); |
+ if (!load->IsLinked()) { |
+ load->set_position(position); |
+ AddInstruction(load); |
} |
- if (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined()); |
+ if (!ast_context()->IsEffect()) Push(load); |
} |
- current_block()->Goto(join); |
+ if (current_block() != NULL) current_block()->Goto(join); |
set_current_block(if_false); |
} |
} |
@@ -4758,8 +4771,6 @@ bool HOptimizedGraphBuilder::TryStorePolymorphicAsMonomorphic( |
// for all maps. Requires special map check on the set of all handled maps. |
if (types->length() > kMaxStorePolymorphism) return false; |
- // TODO(verwaest): Merge the checking logic with the code in |
- // TryLoadPolymorphicAsMonomorphic. |
LookupResult lookup(isolate()); |
int count; |
Representation representation = Representation::None(); |
@@ -5358,73 +5369,6 @@ HInstruction* HOptimizedGraphBuilder::BuildCallGetter( |
} |
-HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( |
- HValue* object, |
- Handle<String> name, |
- 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) { |
- HCheckMaps* checked_object = AddCheckMap(object, map); |
- return New<HLoadNamedField>( |
- checked_object, HObjectAccess::ForArrayLength(map->elements_kind())); |
- } |
- } |
- |
- LookupResult lookup(isolate()); |
- map->LookupDescriptor(NULL, *name, &lookup); |
- if (lookup.IsField()) { |
- HCheckMaps* checked_object = AddCheckMap(object, map); |
- ASSERT(map->IsJSObjectMap()); |
- return BuildLoadNamedField( |
- checked_object, HObjectAccess::ForField(map, &lookup, name)); |
- } |
- |
- // Handle a load of a constant known function. |
- if (lookup.IsConstant()) { |
- AddCheckMap(object, map); |
- Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate()); |
- return New<HConstant>(constant); |
- } |
- |
- if (lookup.IsFound()) { |
- // Cannot handle the property, do a generic load instead. |
- HValue* context = environment()->context(); |
- return new(zone()) HLoadNamedGeneric(context, object, name); |
- } |
- |
- // Handle a load from a known field somewhere in the prototype chain. |
- LookupInPrototypes(map, name, &lookup); |
- if (lookup.IsField()) { |
- Handle<JSObject> prototype(JSObject::cast(map->prototype())); |
- Handle<JSObject> holder(lookup.holder()); |
- Handle<Map> holder_map(holder->map()); |
- AddCheckMap(object, map); |
- HValue* checked_holder = BuildCheckPrototypeMaps(prototype, holder); |
- return BuildLoadNamedField( |
- checked_holder, HObjectAccess::ForField(holder_map, &lookup, name)); |
- } |
- |
- // Handle a load of a constant function somewhere in the prototype chain. |
- if (lookup.IsConstant()) { |
- Handle<JSObject> prototype(JSObject::cast(map->prototype())); |
- Handle<JSObject> holder(lookup.holder()); |
- Handle<Map> holder_map(holder->map()); |
- AddCheckMap(object, map); |
- BuildCheckPrototypeMaps(prototype, holder); |
- Handle<Object> constant(lookup.GetConstantFromMap(*holder_map), isolate()); |
- return New<HConstant>(constant); |
- } |
- |
- // No luck, do a generic load. |
- HValue* context = environment()->context(); |
- return new(zone()) HLoadNamedGeneric(context, object, name); |
-} |
- |
- |
HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object, |
HValue* key) { |
HValue* context = environment()->context(); |
@@ -5824,7 +5768,6 @@ void HOptimizedGraphBuilder::PushLoad(Property* expr, |
static bool AreStringTypes(SmallMapList* types) { |
- if (types == NULL || types->length() == 0) return false; |
for (int i = 0; i < types->length(); i++) { |
if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false; |
} |
@@ -5852,37 +5795,33 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr, |
} else if (expr->key()->IsPropertyName()) { |
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName(); |
- HValue* object = Top(); |
+ HValue* object = Pop(); |
SmallMapList* types; |
- bool monomorphic = ComputeReceiverTypes(expr, object, &types); |
+ ComputeReceiverTypes(expr, object, &types); |
+ ASSERT(types != NULL); |
+ |
+ if (types->length() > 0) { |
+ PropertyAccessInfo info(isolate(), types->first(), name); |
+ if (!info.CanLoadAsMonomorphic(types)) { |
+ return HandlePolymorphicLoadNamedField( |
+ position, ast_id, expr->LoadId(), object, types, name); |
+ } |
- if (monomorphic) { |
- Handle<Map> map = types->first(); |
- Handle<JSFunction> getter; |
- Handle<JSObject> holder; |
- if (LookupGetter(map, name, &getter, &holder)) { |
- AddCheckConstantFunction(holder, Top(), map); |
- if (FLAG_inline_accessors && |
- TryInlineGetter(getter, ast_id, expr->LoadId())) { |
- return; |
- } |
- Add<HPushArgument>(Pop()); |
- instr = new(zone()) HCallConstantFunction(getter, 1); |
+ BuildCheckHeapObject(object); |
+ HInstruction* checked_object; |
+ if (AreStringTypes(types)) { |
+ checked_object = |
+ AddInstruction(HCheckInstanceType::NewIsString(object, zone())); |
} else { |
- instr = BuildLoadNamedMonomorphic(Pop(), name, map); |
+ checked_object = Add<HCheckMaps>(object, types); |
} |
- } else if (AreStringTypes(types) && |
- name->Equals(isolate()->heap()->length_string())) { |
- BuildCheckHeapObject(Pop()); |
- HValue* checked_object = |
- AddInstruction(HCheckInstanceType::NewIsString(object, zone())); |
- instr = BuildLoadStringLength(object, checked_object); |
- } else if (types != NULL && types->length() > 1) { |
- return HandlePolymorphicLoadNamedField( |
- position, ast_id, Pop(), types, name); |
+ instr = BuildLoadMonomorphic( |
+ &info, object, checked_object, ast_id, expr->LoadId()); |
+ if (instr == NULL) return; |
+ if (instr->IsLinked()) return ast_context()->ReturnValue(instr); |
} else { |
- instr = BuildLoadNamedGeneric(Pop(), name, expr); |
+ instr = BuildLoadNamedGeneric(object, name, expr); |
} |
} else { |
@@ -6017,22 +5956,13 @@ bool HOptimizedGraphBuilder::TryCallPolymorphicAsMonomorphic( |
Handle<String> name) { |
if (types->length() > kMaxCallPolymorphism) return false; |
- Handle<Map> map(types->at(0)); |
- LookupResult lookup(isolate()); |
- if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return false; |
- |
- Handle<Object> prototype(map->prototype(), isolate()); |
- for (int count = 1; count < types->length(); ++count) { |
- Handle<Map> test_map(types->at(count)); |
- if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return false; |
- if (test_map->prototype() != *prototype) return false; |
- } |
- |
- if (!expr->ComputeTarget(map, name)) return false; |
+ PropertyAccessInfo info(isolate(), types->at(0), name); |
+ if (!info.CanLoadAsMonomorphic(types)) return false; |
+ if (!expr->ComputeTarget(info.map(), name)) return false; |
BuildCheckHeapObject(receiver); |
Add<HCheckMaps>(receiver, types); |
- AddCheckPrototypeMaps(expr->holder(), map); |
+ AddCheckPrototypeMaps(expr->holder(), info.map()); |
if (FLAG_trace_inlining) { |
Handle<JSFunction> caller = current_info()->closure(); |
SmartArrayPointer<char> caller_name = |