diff options
Diffstat (limited to 'deps/v8/src/compiler/js-native-context-specialization.cc')
-rw-r--r-- | deps/v8/src/compiler/js-native-context-specialization.cc | 305 |
1 files changed, 260 insertions, 45 deletions
diff --git a/deps/v8/src/compiler/js-native-context-specialization.cc b/deps/v8/src/compiler/js-native-context-specialization.cc index 66013b85ca..5a3ccebed1 100644 --- a/deps/v8/src/compiler/js-native-context-specialization.cc +++ b/deps/v8/src/compiler/js-native-context-specialization.cc @@ -24,21 +24,21 @@ namespace compiler { namespace { -bool HasNumberMaps(MapList const& maps) { +bool HasNumberMaps(MapHandles const& maps) { for (auto map : maps) { if (map->instance_type() == HEAP_NUMBER_TYPE) return true; } return false; } -bool HasOnlyJSArrayMaps(MapList const& maps) { +bool HasOnlyJSArrayMaps(MapHandles const& maps) { for (auto map : maps) { if (!map->IsJSArrayMap()) return false; } return true; } -bool HasOnlyNumberMaps(MapList const& maps) { +bool HasOnlyNumberMaps(MapHandles const& maps) { for (auto map : maps) { if (map->instance_type() != HEAP_NUMBER_TYPE) return false; } @@ -154,11 +154,6 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( if (function_prototype->IsConstructor()) { ReplaceWithValue(node, value); return Replace(value); - } else { - node->InsertInput(graph()->zone(), 0, value); - NodeProperties::ChangeOp( - node, javascript()->CallRuntime(Runtime::kThrowNotSuperConstructor)); - return Changed(node); } } @@ -247,9 +242,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { node->ReplaceInput(2, object); node->ReplaceInput(5, effect); NodeProperties::ChangeOp( - node, - javascript()->Call(3, 0.0f, VectorSlotPair(), - ConvertReceiverMode::kNotNullOrUndefined)); + node, javascript()->Call(3, CallFrequency(), VectorSlotPair(), + ConvertReceiverMode::kNotNullOrUndefined)); // Rewire the value uses of {node} to ToBoolean conversion of the result. Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), @@ -271,10 +265,17 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); Node* constructor = NodeProperties::GetValueInput(node, 0); Node* object = NodeProperties::GetValueInput(node, 1); + Node* context = NodeProperties::GetContextInput(node); + Node* frame_state = NodeProperties::GetFrameStateInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); - // Check if the {constructor} is a JSBoundFunction. + // Check if the {constructor} is known at compile time. HeapObjectMatcher m(constructor); - if (m.HasValue() && m.Value()->IsJSBoundFunction()) { + if (!m.HasValue()) return NoChange(); + + // Check if the {constructor} is a JSBoundFunction. + if (m.Value()->IsJSBoundFunction()) { // OrdinaryHasInstance on bound functions turns into a recursive // invocation of the instanceof operator again. // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. @@ -288,6 +289,160 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( return reduction.Changed() ? reduction : Changed(node); } + // Check if the {constructor} is a JSFunction. + if (m.Value()->IsJSFunction()) { + // Check if the {function} is a constructor and has an instance "prototype". + Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); + if (function->IsConstructor() && function->has_instance_prototype() && + function->prototype()->IsJSReceiver()) { + // Ensure that the {function} has a valid initial map, so we can + // depend on that for the prototype constant-folding below. + JSFunction::EnsureHasInitialMap(function); + + // Install a code dependency on the {function}s initial map. + Handle<Map> initial_map(function->initial_map(), isolate()); + dependencies()->AssumeInitialMapCantChange(initial_map); + Handle<JSReceiver> function_prototype = + handle(JSReceiver::cast(initial_map->prototype()), isolate()); + + // Check if we can constant-fold the prototype chain walk + // for the given {object} and the {function_prototype}. + InferHasInPrototypeChainResult result = + InferHasInPrototypeChain(object, effect, function_prototype); + if (result != kMayBeInPrototypeChain) { + Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } + + Node* prototype = jsgraph()->Constant(function_prototype); + + Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), object); + 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 {object}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 = object = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + object, object, loop); + + // Load the {object} map and instance type. + Node* object_map = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + object, effect, control); + Node* object_instance_type = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapInstanceType()), + object_map, effect, control); + + // Check if the {object} is a special receiver, because for special + // receivers, i.e. proxies or API objects that need access checks, + // we have to use the %HasInPrototypeChain runtime function instead. + Node* check1 = graph()->NewNode( + simplified()->NumberLessThanOrEqual(), object_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 {object} is not a receiver at all. + Node* check10 = + graph()->NewNode(simplified()->NumberLessThan(), object_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), object, + 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 {object} prototype. + Node* object_prototype = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapPrototype()), object_map, + effect, control); + + // Check if we reached the end of {object}s prototype chain. + Node* check2 = + graph()->NewNode(simplified()->ReferenceEqual(), object_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(), + object_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, object_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); + } + } + return NoChange(); } @@ -551,9 +706,8 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { } Reduction JSNativeContextSpecialization::ReduceNamedAccess( - Node* node, Node* value, MapHandleList const& receiver_maps, - Handle<Name> name, AccessMode access_mode, LanguageMode language_mode, - Node* index) { + Node* node, Node* value, MapHandles const& receiver_maps, Handle<Name> name, + AccessMode access_mode, LanguageMode language_mode, Node* index) { DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || node->opcode() == IrOpcode::kJSStoreNamed || node->opcode() == IrOpcode::kJSLoadProperty || @@ -568,8 +722,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( // Check if we have an access o.x or o.x=v where o is the current // native contexts' global proxy, and turn that into a direct access // to the current native contexts' global object instead. - if (receiver_maps.length() == 1) { - Handle<Map> receiver_map = receiver_maps.first(); + if (receiver_maps.size() == 1) { + Handle<Map> receiver_map = receiver_maps.front(); if (receiver_map->IsJSGlobalProxyMap()) { Object* maybe_constructor = receiver_map->GetConstructor(); // Detached global proxies have |null| as their constructor. @@ -686,7 +840,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( Node* this_control = fallthrough_control; // Perform map check on {receiver}. - MapList const& receiver_maps = access_info.receiver_maps(); + MapHandles const& receiver_maps = access_info.receiver_maps(); { // Emit a (sequence of) map checks for other {receiver}s. ZoneVector<Node*> this_controls(zone()); @@ -801,10 +955,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( } // Extract receiver maps from the IC using the {nexus}. - MapHandleList receiver_maps; + MapHandles receiver_maps; if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { return NoChange(); - } else if (receiver_maps.length() == 0) { + } else if (receiver_maps.empty()) { if (flags() & kBailoutOnUninitialized) { return ReduceSoftDeoptimize( node, @@ -831,11 +985,11 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { p.name().is_identical_to(factory()->prototype_string())) { // Optimize "prototype" property of functions. Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); - if (function->has_initial_map()) { + if (function->IsConstructor()) { // We need to add a code dependency on the initial map of the // {function} in order to be notified about changes to the - // "prototype" of {function}, so it doesn't make sense to - // continue unless deoptimization is enabled. + // "prototype" of {function}. + JSFunction::EnsureHasInitialMap(function); Handle<Map> initial_map(function->initial_map(), isolate()); dependencies()->AssumeInitialMapCantChange(initial_map); Handle<Object> prototype(function->prototype(), isolate()); @@ -892,7 +1046,7 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { } Reduction JSNativeContextSpecialization::ReduceElementAccess( - Node* node, Node* index, Node* value, MapHandleList const& receiver_maps, + Node* node, Node* index, Node* value, MapHandles const& receiver_maps, AccessMode access_mode, LanguageMode language_mode, KeyedAccessStoreMode store_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || @@ -1056,7 +1210,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( receiver, this_effect, this_control); // Perform map check(s) on {receiver}. - MapList const& receiver_maps = access_info.receiver_maps(); + MapHandles const& receiver_maps = access_info.receiver_maps(); if (j == access_infos.size() - 1) { // Last map check on the fallthrough control path, do a // conditional eager deoptimization exit here. @@ -1189,10 +1343,10 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess( } // Extract receiver maps from the {nexus}. - MapHandleList receiver_maps; + MapHandles receiver_maps; if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { return NoChange(); - } else if (receiver_maps.length() == 0) { + } else if (receiver_maps.empty()) { if (flags() & kBailoutOnUninitialized) { return ReduceSoftDeoptimize( node, @@ -1347,7 +1501,7 @@ JSNativeContextSpecialization::BuildPropertyAccess( // Introduce the call to the getter function. if (access_info.constant()->IsJSFunction()) { value = effect = control = graph()->NewNode( - javascript()->Call(2, 0.0f, VectorSlotPair(), + javascript()->Call(2, CallFrequency(), VectorSlotPair(), ConvertReceiverMode::kNotNullOrUndefined), target, receiver, context, frame_state0, effect, control); } else { @@ -1383,7 +1537,7 @@ JSNativeContextSpecialization::BuildPropertyAccess( // Introduce the call to the setter function. if (access_info.constant()->IsJSFunction()) { effect = control = graph()->NewNode( - javascript()->Call(3, 0.0f, VectorSlotPair(), + javascript()->Call(3, CallFrequency(), VectorSlotPair(), ConvertReceiverMode::kNotNullOrUndefined), target, receiver, value, context, frame_state0, effect, control); } else { @@ -1756,7 +1910,7 @@ JSNativeContextSpecialization::BuildElementAccess( // TODO(bmeurer): We currently specialize based on elements kind. We should // also be able to properly support strings and other JSObjects here. ElementsKind elements_kind = access_info.elements_kind(); - MapList const& receiver_maps = access_info.receiver_maps(); + MapHandles const& receiver_maps = access_info.receiver_maps(); if (IsFixedTypedArrayElementsKind(elements_kind)) { Node* buffer; @@ -2059,7 +2213,7 @@ JSNativeContextSpecialization::InlineApiCall( int const argc = value == nullptr ? 0 : 1; // The stub always expects the receiver as the first param on the stack. CallApiCallbackStub stub( - isolate(), argc, call_data_object->IsUndefined(isolate()), + isolate(), argc, true /* FunctionTemplateInfo doesn't have an associated context. */); CallInterfaceDescriptor call_interface_descriptor = stub.GetCallInterfaceDescriptor(); @@ -2127,7 +2281,7 @@ Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver, Node* JSNativeContextSpecialization::BuildCheckMaps( Node* receiver, Node* effect, Node* control, - std::vector<Handle<Map>> const& receiver_maps) { + MapHandles const& receiver_maps) { HeapObjectMatcher m(receiver); if (m.HasValue()) { Handle<Map> receiver_map(m.Value()->map(), isolate()); @@ -2154,6 +2308,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps( Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( Handle<Map> map, Node* properties, Node* effect, Node* control) { + // TODO(bmeurer/jkummerow): Property deletions can undo map transitions + // while keeping the backing store around, meaning that even though the + // map might believe that objects have no unused property fields, there + // might actually be some. It would be nice to not create a new backing + // store in that case (i.e. when properties->length() >= new_length). + // However, introducing branches and Phi nodes here would make it more + // difficult for escape analysis to get rid of the backing stores used + // for intermediate states of chains of property additions. That makes + // it unclear what the best approach is here. DCHECK_EQ(0, map->unused_property_fields()); // Compute the length of the old {properties} and the new properties. int length = map->NextFreePropertyIndex() - map->GetInObjectProperties(); @@ -2192,7 +2355,7 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( } void JSNativeContextSpecialization::AssumePrototypesStable( - std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) { + MapHandles const& receiver_maps, Handle<JSObject> holder) { // Determine actual holder and perform prototype chain checks. for (auto map : receiver_maps) { // Perform the implicit ToObject for primitives here. @@ -2207,7 +2370,7 @@ void JSNativeContextSpecialization::AssumePrototypesStable( } bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( - std::vector<Handle<Map>> const& receiver_maps) { + MapHandles const& receiver_maps) { // Check if the array prototype chain is intact. if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false; @@ -2241,10 +2404,61 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( return true; } +JSNativeContextSpecialization::InferHasInPrototypeChainResult +JSNativeContextSpecialization::InferHasInPrototypeChain( + Node* receiver, Node* effect, Handle<JSReceiver> 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<JSReceiver> const current = + PrototypeIterator::GetCurrent<JSReceiver>(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; +} + bool JSNativeContextSpecialization::ExtractReceiverMaps( Node* receiver, Node* effect, FeedbackNexus const& nexus, - MapHandleList* receiver_maps) { - DCHECK_EQ(0, receiver_maps->length()); + MapHandles* receiver_maps) { + DCHECK_EQ(0, receiver_maps->size()); // See if we can infer a concrete type for the {receiver}. if (InferReceiverMaps(receiver, effect, receiver_maps)) { // We can assume that the {receiver} still has the infered {receiver_maps}. @@ -2255,11 +2469,12 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps( // Try to filter impossible candidates based on infered root map. Handle<Map> receiver_map; if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) { - for (int i = receiver_maps->length(); --i >= 0;) { - if (receiver_maps->at(i)->FindRootMap() != *receiver_map) { - receiver_maps->Remove(i); - } - } + receiver_maps->erase( + std::remove_if(receiver_maps->begin(), receiver_maps->end(), + [receiver_map](const Handle<Map>& map) { + return map->FindRootMap() != *receiver_map; + }), + receiver_maps->end()); } return true; } @@ -2267,13 +2482,13 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps( } bool JSNativeContextSpecialization::InferReceiverMaps( - Node* receiver, Node* effect, MapHandleList* receiver_maps) { + Node* receiver, Node* effect, MapHandles* receiver_maps) { ZoneHandleSet<Map> maps; NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(receiver, effect, &maps); if (result == NodeProperties::kReliableReceiverMaps) { for (size_t i = 0; i < maps.size(); ++i) { - receiver_maps->Add(maps[i]); + receiver_maps->push_back(maps[i]); } return true; } else if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -2283,7 +2498,7 @@ bool JSNativeContextSpecialization::InferReceiverMaps( if (!maps[i]->is_stable()) return false; } for (size_t i = 0; i < maps.size(); ++i) { - receiver_maps->Add(maps[i]); + receiver_maps->push_back(maps[i]); } return true; } |