Index: src/compiler/js-typed-lowering.cc |
diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc |
index fce00b776ec8bde7cf80f2f79e057b9296838041..c35691918c35e71a61786c2e8337b8f8a55589cb 100644 |
--- a/src/compiler/js-typed-lowering.cc |
+++ b/src/compiler/js-typed-lowering.cc |
@@ -1390,6 +1390,210 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { |
return NoChange(); |
} |
+JSTypedLowering::InferHasInPrototypeChainResult |
+JSTypedLowering::InferHasInPrototypeChain(Node* receiver, Node* effect, |
+ Handle<HeapObject> prototype) { |
+ ZoneHandleSet<Map> receiver_maps; |
+ NodeProperties::InferReceiverMapsResult result = |
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); |
+ if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain; |
+ |
+ // Check if either all or none of the {receiver_maps} have the given |
+ // {prototype} in their prototype chain. |
+ bool all = true; |
+ bool none = true; |
+ for (size_t i = 0; i < receiver_maps.size(); ++i) { |
+ Handle<Map> receiver_map = receiver_maps[i]; |
+ if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
+ return kMayBeInPrototypeChain; |
+ } |
+ if (result == NodeProperties::kUnreliableReceiverMaps) { |
+ // In case of an unreliable {result} we need to ensure that all |
+ // {receiver_maps} are stable, because otherwise we cannot trust |
+ // the {receiver_maps} information, since arbitrary side-effects |
+ // may have happened. |
+ if (!receiver_map->is_stable()) { |
+ return kMayBeInPrototypeChain; |
+ } |
+ } |
+ for (PrototypeIterator j(receiver_map);; j.Advance()) { |
+ if (j.IsAtEnd()) { |
+ all = false; |
+ break; |
+ } |
+ Handle<HeapObject> const current = |
+ PrototypeIterator::GetCurrent<HeapObject>(j); |
+ if (current.is_identical_to(prototype)) { |
+ none = false; |
+ break; |
+ } |
+ if (!current->map()->is_stable() || |
+ current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
+ return kMayBeInPrototypeChain; |
+ } |
+ } |
+ } |
+ DCHECK_IMPLIES(all, !none); |
+ DCHECK_IMPLIES(none, !all); |
+ |
+ if (all) return kIsInPrototypeChain; |
+ if (none) return kIsNotInPrototypeChain; |
+ return kMayBeInPrototypeChain; |
+} |
+ |
+Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) { |
+ DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode()); |
+ Node* value = NodeProperties::GetValueInput(node, 0); |
+ Type* value_type = NodeProperties::GetType(value); |
+ Node* prototype = NodeProperties::GetValueInput(node, 1); |
+ Type* prototype_type = NodeProperties::GetType(prototype); |
+ Node* context = NodeProperties::GetContextInput(node); |
+ Node* frame_state = NodeProperties::GetFrameStateInput(node); |
+ Node* effect = NodeProperties::GetEffectInput(node); |
+ Node* control = NodeProperties::GetControlInput(node); |
+ |
+ // If {value} cannot be a receiver, then it cannot have {prototype} in |
+ // it's prototype chain (all Primitive values have a null prototype). |
+ if (value_type->Is(Type::Primitive())) { |
+ Node* value = jsgraph()->FalseConstant(); |
+ ReplaceWithValue(node, value, effect, control); |
+ return Replace(value); |
+ } |
+ |
+ // Check if we can constant-fold the prototype chain walk |
+ // for the given {value} and the {prototype}. |
+ if (prototype_type->IsHeapConstant()) { |
+ InferHasInPrototypeChainResult result = InferHasInPrototypeChain( |
+ value, effect, prototype_type->AsHeapConstant()->Value()); |
+ if (result != kMayBeInPrototypeChain) { |
+ Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain); |
+ ReplaceWithValue(node, value, effect, control); |
+ return Replace(value); |
+ } |
+ } |
+ |
+ Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
+ Node* branch0 = |
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); |
+ |
+ Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
+ Node* etrue0 = effect; |
+ Node* vtrue0 = jsgraph()->FalseConstant(); |
+ |
+ control = graph()->NewNode(common()->IfFalse(), branch0); |
+ |
+ // Loop through the {value}s prototype chain looking for the {prototype}. |
+ Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); |
+ Node* eloop = effect = |
+ graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
+ Node* vloop = value = graph()->NewNode( |
+ common()->Phi(MachineRepresentation::kTagged, 2), value, value, loop); |
+ NodeProperties::SetType(vloop, Type::NonInternal()); |
+ |
+ // Load the {value} map and instance type. |
+ Node* value_map = effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control); |
+ Node* value_instance_type = effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map, |
+ effect, control); |
+ |
+ // Check if the {value} is a special receiver, because for special |
+ // receivers, i.e. proxies or API values that need access checks, |
+ // we have to use the %HasInPrototypeChain runtime function instead. |
+ Node* check1 = graph()->NewNode( |
+ simplified()->NumberLessThanOrEqual(), value_instance_type, |
+ jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE)); |
+ Node* branch1 = |
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check1, control); |
+ |
+ control = graph()->NewNode(common()->IfFalse(), branch1); |
+ |
+ Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
+ Node* etrue1 = effect; |
+ Node* vtrue1; |
+ |
+ // Check if the {value} is not a receiver at all. |
+ Node* check10 = |
+ graph()->NewNode(simplified()->NumberLessThan(), value_instance_type, |
+ jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE)); |
+ Node* branch10 = |
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check10, if_true1); |
+ |
+ // A primitive value cannot match the {prototype} we're looking for. |
+ if_true1 = graph()->NewNode(common()->IfTrue(), branch10); |
+ vtrue1 = jsgraph()->FalseConstant(); |
+ |
+ Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10); |
+ Node* efalse1 = etrue1; |
+ Node* vfalse1; |
+ { |
+ // Slow path, need to call the %HasInPrototypeChain runtime function. |
+ vfalse1 = efalse1 = if_false1 = graph()->NewNode( |
+ javascript()->CallRuntime(Runtime::kHasInPrototypeChain), value, |
+ prototype, context, frame_state, efalse1, if_false1); |
+ |
+ // Replace any potential {IfException} uses of {node} to catch |
+ // exceptions from this %HasInPrototypeChain runtime call instead. |
+ Node* on_exception = nullptr; |
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
+ NodeProperties::ReplaceControlInput(on_exception, vfalse1); |
+ NodeProperties::ReplaceEffectInput(on_exception, efalse1); |
+ if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1); |
+ Revisit(on_exception); |
+ } |
+ } |
+ |
+ // Load the {value} prototype. |
+ Node* value_prototype = effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForMapPrototype()), value_map, |
+ effect, control); |
+ |
+ // Check if we reached the end of {value}s prototype chain. |
+ Node* check2 = graph()->NewNode(simplified()->ReferenceEqual(), |
+ value_prototype, jsgraph()->NullConstant()); |
+ Node* branch2 = graph()->NewNode(common()->Branch(), check2, control); |
+ |
+ Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
+ Node* etrue2 = effect; |
+ Node* vtrue2 = jsgraph()->FalseConstant(); |
+ |
+ control = graph()->NewNode(common()->IfFalse(), branch2); |
+ |
+ // Check if we reached the {prototype}. |
+ Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(), |
+ value_prototype, prototype); |
+ Node* branch3 = graph()->NewNode(common()->Branch(), check3, control); |
+ |
+ Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3); |
+ Node* etrue3 = effect; |
+ Node* vtrue3 = jsgraph()->TrueConstant(); |
+ |
+ control = graph()->NewNode(common()->IfFalse(), branch3); |
+ |
+ // Close the loop. |
+ vloop->ReplaceInput(1, value_prototype); |
+ eloop->ReplaceInput(1, effect); |
+ loop->ReplaceInput(1, control); |
+ |
+ control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, if_true2, |
+ if_true3, if_false1); |
+ effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2, |
+ etrue3, efalse1, control); |
+ |
+ // Morph the {node} into an appropriate Phi. |
+ ReplaceWithValue(node, node, effect, control); |
+ node->ReplaceInput(0, vtrue0); |
+ node->ReplaceInput(1, vtrue1); |
+ node->ReplaceInput(2, vtrue2); |
+ node->ReplaceInput(3, vtrue3); |
+ node->ReplaceInput(4, vfalse1); |
+ node->ReplaceInput(5, control); |
+ node->TrimInputCount(6); |
+ NodeProperties::ChangeOp(node, |
+ common()->Phi(MachineRepresentation::kTagged, 5)); |
+ return Changed(node); |
+} |
+ |
Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) { |
DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); |
Node* constructor = NodeProperties::GetValueInput(node, 0); |
@@ -2247,6 +2451,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { |
case IrOpcode::kJSDivide: |
case IrOpcode::kJSModulus: |
return ReduceNumberBinop(node); |
+ case IrOpcode::kJSHasInPrototypeChain: |
+ return ReduceJSHasInPrototypeChain(node); |
case IrOpcode::kJSOrdinaryHasInstance: |
return ReduceJSOrdinaryHasInstance(node); |
case IrOpcode::kJSToBoolean: |