| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 6043caa0f47f84ed16c18535eed3f278204b17b3..d329aea7de3da188b7eed67fb3c034f4620da2f1 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());
|
| + // 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);
|
| + 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);
|
| + } 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 (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined());
|
| + }
|
| +
|
| + current_block()->Goto(join);
|
| + 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);
|
| + load->set_position(expr->position());
|
| + AddInstruction(load);
|
| + if (!ast_context()->IsEffect()) Push(load);
|
| +
|
| + if (join != NULL) {
|
| + current_block()->Goto(join);
|
| + } else {
|
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| + if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| + return;
|
| + }
|
| + }
|
| +
|
| + ASSERT(join != NULL);
|
| + join->SetJoinId(expr->id());
|
| + set_current_block(join);
|
| + if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
|
|