Chromium Code Reviews| Index: src/hydrogen.cc |
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
| index 6043caa0f47f84ed16c18535eed3f278204b17b3..da2dbf4faa5dfb7a4e50f0a7f5ac60ad1992aa41 100644 |
| --- a/src/hydrogen.cc |
| +++ b/src/hydrogen.cc |
| @@ -4613,6 +4613,36 @@ HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic( |
| } |
| +// 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)->map()->has_named_interceptor() || |
| + JSObject::cast(current)->IsAccessCheckNeeded() || |
| + !JSObject::cast(current)->HasFastProperties()) { |
| + return false; |
| + } |
| + |
| + 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(); |
| + } |
| + return true; |
| +} |
| + |
| + |
| void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( |
| Property* expr, |
| HValue* object, |
| @@ -4620,16 +4650,90 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( |
| Handle<String> name) { |
| HInstruction* instr = TryLoadPolymorphicAsMonomorphic( |
| expr, object, types, name); |
| - if (instr == NULL) { |
| - // Something did not match; must use a polymorphic load. |
| - BuildCheckHeapObject(object); |
| - HValue* context = environment()->context(); |
| - instr = new(zone()) HLoadNamedFieldPolymorphic( |
| - context, object, types, name, zone()); |
| + if (instr != NULL) { |
| + instr->set_position(expr->position()); |
| + return ast_context()->ReturnInstruction(instr, expr->id()); |
| } |
| - instr->set_position(expr->position()); |
| - return ast_context()->ReturnInstruction(instr, expr->id()); |
| + NoObservableSideEffectsScope no_side_effects(this); |
|
ulan
2013/08/06 13:36:24
Shouldn't this be in its own scope (i.e. surrounde
|
| + // 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() && |
| + !map->is_dictionary_map() && |
| + !map->has_named_interceptor() && |
| + (lookup.IsConstant() || |
| + (!lookup.IsFound() && |
| + PrototypeChainCanNeverResolve(map, name))))) { |
| + if (count == 0) { |
| + BuildCheckHeapObject(object); |
| + join = graph()->CreateBasicBlock(); |
| + } |
| + ++count; |
| + HBasicBlock* if_true = graph()->CreateBasicBlock(); |
| + HBasicBlock* if_false = graph()->CreateBasicBlock(); |
| + HCompareMap* compare = |
| + new(zone()) HCompareMap(object, 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(object, access); |
| + load->set_position(expr->position()); |
| + AddInstruction(load); |
| + Push(load); |
| + } else if (lookup.IsConstant()) { |
| + Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate()); |
| + HConstant* hconstant = Add<HConstant>(constant); |
| + Push(hconstant); |
| + } 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); |
| + } |
| + Push(graph()->GetConstantUndefined()); |
| + } |
| + |
| + current_block()->Goto(join, NULL, false); |
| + set_current_block(if_false); |
| + } |
| + } |
| + |
| + // Finish up. Unconditionally deoptimize if we've handled all the maps we |
| + // know about and do not want to handle ones we've never seen. Otherwise |
| + // use a generic IC. |
| + if (count == types->length() && FLAG_deoptimize_uncommon_cases) { |
| + FinishExitWithHardDeoptimization(join); |
| + } else { |
| + HInstruction* load = BuildLoadNamedGeneric(object, name, expr); |
|
ulan
2013/08/06 13:36:24
Why it is in NoObservableSideEffectsScope scope?
Toon Verwaest
2013/08/06 14:03:44
Scary premature optimization attempt. Removed :)
|
| + load->set_position(expr->position()); |
| + AddInstruction(load); |
| + Push(load); |
| + |
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE); |
| + |
| + if (join != NULL) { |
| + current_block()->Goto(join, NULL, false); |
| + } else { |
| + return ast_context()->ReturnValue(Pop()); |
| + } |
| + } |
| + |
| + ASSERT(join != NULL); |
| + set_current_block(join); |
| + return ast_context()->ReturnValue(Pop()); |
| } |