Index: src/hydrogen-instructions.cc |
=================================================================== |
--- src/hydrogen-instructions.cc (revision 11774) |
+++ src/hydrogen-instructions.cc (working copy) |
@@ -1618,6 +1618,38 @@ |
} |
+// 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() || |
+ JSObject::cast(current)->IsAccessCheckNeeded() || |
+ !JSObject::cast(current)->HasFastProperties()) { |
+ return false; |
+ } |
+ |
+ LookupResult lookup(isolate); |
+ JSObject::cast(current)->map()->LookupInDescriptors(NULL, *name, &lookup); |
+ if (lookup.IsFound()) { |
+ if (lookup.type() != MAP_TRANSITION) return false; |
+ } else if (!lookup.IsCacheable()) { |
+ return false; |
+ } |
+ |
+ current = JSObject::cast(current)->GetPrototype(); |
+ } |
+ return true; |
+} |
+ |
+ |
HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context, |
HValue* object, |
SmallMapList* types, |
@@ -1630,7 +1662,7 @@ |
SetOperandAt(1, object); |
set_representation(Representation::Tagged()); |
SetGVNFlag(kDependsOnMaps); |
- int map_transitions = 0; |
+ SmallMapList negative_lookups; |
for (int i = 0; |
i < types->length() && types_.length() < kMaxLoadPolymorphism; |
++i) { |
@@ -1653,21 +1685,32 @@ |
types_.Add(types->at(i), zone); |
break; |
case MAP_TRANSITION: |
- // We should just ignore these since they are not relevant to a load |
- // operation. This means we will deopt if we actually see this map |
- // from optimized code. |
- map_transitions++; |
+ if (PrototypeChainCanNeverResolve(map, name)) { |
+ negative_lookups.Add(types->at(i), zone); |
+ } |
break; |
default: |
break; |
} |
+ } else if (lookup.IsCacheable()) { |
+ if (PrototypeChainCanNeverResolve(map, name)) { |
+ negative_lookups.Add(types->at(i), zone); |
+ } |
} |
} |
- if (types_.length() + map_transitions == types->length() && |
- FLAG_deoptimize_uncommon_cases) { |
+ bool need_generic = |
+ (types->length() != negative_lookups.length() + types_.length()); |
+ if (!need_generic && FLAG_deoptimize_uncommon_cases) { |
SetFlag(kUseGVN); |
+ for (int i = 0; i < negative_lookups.length(); i++) { |
+ types_.Add(negative_lookups.at(i), zone); |
+ } |
} else { |
+ // We don't have an easy way to handle both a call (to the generic stub) and |
+ // a deopt in the same hydrogen instruction, so in this case we don't add |
+ // the negative lookups which can deopt - just let the generic stub handle |
+ // them. |
SetAllSideEffects(); |
need_generic_ = true; |
} |