|  | // Copyright 2015 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/compiler/js-native-context-specialization.h" | 
|  |  | 
|  | #include "src/accessors.h" | 
|  | #include "src/api-inl.h" | 
|  | #include "src/code-factory.h" | 
|  | #include "src/compiler/access-builder.h" | 
|  | #include "src/compiler/access-info.h" | 
|  | #include "src/compiler/allocation-builder.h" | 
|  | #include "src/compiler/compilation-dependencies.h" | 
|  | #include "src/compiler/js-graph.h" | 
|  | #include "src/compiler/js-operator.h" | 
|  | #include "src/compiler/linkage.h" | 
|  | #include "src/compiler/node-matchers.h" | 
|  | #include "src/compiler/property-access-builder.h" | 
|  | #include "src/compiler/type-cache.h" | 
|  | #include "src/feedback-vector.h" | 
|  | #include "src/field-index-inl.h" | 
|  | #include "src/isolate-inl.h" | 
|  | #include "src/objects/js-array-inl.h" | 
|  | #include "src/objects/templates.h" | 
|  | #include "src/vector-slot-pair.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  | namespace compiler { | 
|  |  | 
|  | // This is needed for gc_mole which will compile this file without the full set | 
|  | // of GN defined macros. | 
|  | #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP | 
|  | #define V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP 64 | 
|  | #endif | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool HasNumberMaps(MapHandles const& maps) { | 
|  | for (auto map : maps) { | 
|  | if (map->instance_type() == HEAP_NUMBER_TYPE) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool HasOnlyJSArrayMaps(MapHandles const& maps) { | 
|  | for (auto map : maps) { | 
|  | if (!map->IsJSArrayMap()) return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | struct JSNativeContextSpecialization::ScriptContextTableLookupResult { | 
|  | Handle<Context> context; | 
|  | bool immutable; | 
|  | int index; | 
|  | }; | 
|  |  | 
|  | JSNativeContextSpecialization::JSNativeContextSpecialization( | 
|  | Editor* editor, JSGraph* jsgraph, JSHeapBroker* js_heap_broker, Flags flags, | 
|  | Handle<Context> native_context, CompilationDependencies* dependencies, | 
|  | Zone* zone) | 
|  | : AdvancedReducer(editor), | 
|  | jsgraph_(jsgraph), | 
|  | js_heap_broker_(js_heap_broker), | 
|  | flags_(flags), | 
|  | global_object_(native_context->global_object(), jsgraph->isolate()), | 
|  | global_proxy_(JSGlobalProxy::cast(native_context->global_proxy()), | 
|  | jsgraph->isolate()), | 
|  | native_context_(js_heap_broker, native_context), | 
|  | dependencies_(dependencies), | 
|  | zone_(zone), | 
|  | type_cache_(TypeCache::Get()) {} | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::Reduce(Node* node) { | 
|  | switch (node->opcode()) { | 
|  | case IrOpcode::kJSAdd: | 
|  | return ReduceJSAdd(node); | 
|  | case IrOpcode::kJSGetSuperConstructor: | 
|  | return ReduceJSGetSuperConstructor(node); | 
|  | case IrOpcode::kJSInstanceOf: | 
|  | return ReduceJSInstanceOf(node); | 
|  | case IrOpcode::kJSHasInPrototypeChain: | 
|  | return ReduceJSHasInPrototypeChain(node); | 
|  | case IrOpcode::kJSOrdinaryHasInstance: | 
|  | return ReduceJSOrdinaryHasInstance(node); | 
|  | case IrOpcode::kJSPromiseResolve: | 
|  | return ReduceJSPromiseResolve(node); | 
|  | case IrOpcode::kJSResolvePromise: | 
|  | return ReduceJSResolvePromise(node); | 
|  | case IrOpcode::kJSLoadContext: | 
|  | return ReduceJSLoadContext(node); | 
|  | case IrOpcode::kJSLoadGlobal: | 
|  | return ReduceJSLoadGlobal(node); | 
|  | case IrOpcode::kJSStoreGlobal: | 
|  | return ReduceJSStoreGlobal(node); | 
|  | case IrOpcode::kJSLoadNamed: | 
|  | return ReduceJSLoadNamed(node); | 
|  | case IrOpcode::kJSStoreNamed: | 
|  | return ReduceJSStoreNamed(node); | 
|  | case IrOpcode::kJSLoadProperty: | 
|  | return ReduceJSLoadProperty(node); | 
|  | case IrOpcode::kJSStoreProperty: | 
|  | return ReduceJSStoreProperty(node); | 
|  | case IrOpcode::kJSStoreNamedOwn: | 
|  | return ReduceJSStoreNamedOwn(node); | 
|  | case IrOpcode::kJSStoreDataPropertyInLiteral: | 
|  | return ReduceJSStoreDataPropertyInLiteral(node); | 
|  | case IrOpcode::kJSStoreInArrayLiteral: | 
|  | return ReduceJSStoreInArrayLiteral(node); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) { | 
|  | // TODO(turbofan): This has to run together with the inlining and | 
|  | // native context specialization to be able to leverage the string | 
|  | // constant-folding for optimizing property access, but we should | 
|  | // nevertheless find a better home for this at some point. | 
|  | DCHECK_EQ(IrOpcode::kJSAdd, node->opcode()); | 
|  |  | 
|  | // Constant-fold string concatenation. | 
|  | HeapObjectBinopMatcher m(node); | 
|  | if (m.left().HasValue() && m.left().Value()->IsString() && | 
|  | m.right().HasValue() && m.right().Value()->IsString()) { | 
|  | Handle<String> left = Handle<String>::cast(m.left().Value()); | 
|  | Handle<String> right = Handle<String>::cast(m.right().Value()); | 
|  | if (left->length() + right->length() <= String::kMaxLength) { | 
|  | Handle<String> result = | 
|  | factory()->NewConsString(left, right).ToHandleChecked(); | 
|  | Node* value = jsgraph()->HeapConstant(result); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( | 
|  | Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode()); | 
|  | Node* constructor = NodeProperties::GetValueInput(node, 0); | 
|  |  | 
|  | // Check if the input is a known JSFunction. | 
|  | HeapObjectMatcher m(constructor); | 
|  | if (!m.HasValue()) return NoChange(); | 
|  | Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); | 
|  | Handle<Map> function_map(function->map(), isolate()); | 
|  | Handle<Object> function_prototype(function_map->prototype(), isolate()); | 
|  |  | 
|  | // We can constant-fold the super constructor access if the | 
|  | // {function}s map is stable, i.e. we can use a code dependency | 
|  | // to guard against [[Prototype]] changes of {function}. | 
|  | if (function_map->is_stable() && function_prototype->IsConstructor()) { | 
|  | dependencies()->DependOnStableMap(MapRef(js_heap_broker(), function_map)); | 
|  | Node* value = jsgraph()->Constant(function_prototype); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); | 
|  | FeedbackParameter const& p = FeedbackParameterOf(node->op()); | 
|  | Node* object = NodeProperties::GetValueInput(node, 0); | 
|  | Node* constructor = NodeProperties::GetValueInput(node, 1); | 
|  | Node* context = NodeProperties::GetContextInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* frame_state = NodeProperties::GetFrameStateInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Check if the right hand side is a known {receiver}, or | 
|  | // we have feedback from the InstanceOfIC. | 
|  | Handle<JSObject> receiver; | 
|  | HeapObjectMatcher m(constructor); | 
|  | if (m.HasValue() && m.Value()->IsJSObject()) { | 
|  | receiver = Handle<JSObject>::cast(m.Value()); | 
|  | } else if (p.feedback().IsValid()) { | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  | if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) return NoChange(); | 
|  | } else { | 
|  | return NoChange(); | 
|  | } | 
|  | Handle<Map> receiver_map(receiver->map(), isolate()); | 
|  |  | 
|  | // Compute property access info for @@hasInstance on {receiver}. | 
|  | PropertyAccessInfo access_info; | 
|  | AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), | 
|  | native_context().object<Context>(), | 
|  | graph()->zone()); | 
|  | if (!access_info_factory.ComputePropertyAccessInfo( | 
|  | receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad, | 
|  | &access_info)) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  |  | 
|  | if (access_info.IsNotFound()) { | 
|  | // If there's no @@hasInstance handler, the OrdinaryHasInstance operation | 
|  | // takes over, but that requires the {receiver} to be callable. | 
|  | if (receiver->IsCallable()) { | 
|  | // Determine actual holder and perform prototype chain checks. | 
|  | Handle<JSObject> holder; | 
|  | if (access_info.holder().ToHandle(&holder)) { | 
|  | dependencies()->DependOnStablePrototypeChains( | 
|  | js_heap_broker(), native_context().object<Context>(), | 
|  | access_info.receiver_maps(), holder); | 
|  | } | 
|  |  | 
|  | // Check that {constructor} is actually {receiver}. | 
|  | constructor = access_builder.BuildCheckValue(constructor, &effect, | 
|  | control, receiver); | 
|  |  | 
|  | // Monomorphic property access. | 
|  | access_builder.BuildCheckMaps(constructor, &effect, control, | 
|  | access_info.receiver_maps()); | 
|  |  | 
|  | // Lower to OrdinaryHasInstance(C, O). | 
|  | NodeProperties::ReplaceValueInput(node, constructor, 0); | 
|  | NodeProperties::ReplaceValueInput(node, object, 1); | 
|  | NodeProperties::ReplaceEffectInput(node, effect); | 
|  | NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); | 
|  | Reduction const reduction = ReduceJSOrdinaryHasInstance(node); | 
|  | return reduction.Changed() ? reduction : Changed(node); | 
|  | } | 
|  | } else if (access_info.IsDataConstant() || | 
|  | access_info.IsDataConstantField()) { | 
|  | // Determine actual holder and perform prototype chain checks. | 
|  | Handle<JSObject> holder; | 
|  | if (access_info.holder().ToHandle(&holder)) { | 
|  | dependencies()->DependOnStablePrototypeChains( | 
|  | js_heap_broker(), native_context().object<Context>(), | 
|  | access_info.receiver_maps(), holder); | 
|  | } else { | 
|  | holder = receiver; | 
|  | } | 
|  |  | 
|  | Handle<Object> constant; | 
|  | if (access_info.IsDataConstant()) { | 
|  | DCHECK(!FLAG_track_constant_fields); | 
|  | constant = access_info.constant(); | 
|  | } else { | 
|  | DCHECK(FLAG_track_constant_fields); | 
|  | DCHECK(access_info.IsDataConstantField()); | 
|  | // The value must be callable therefore tagged. | 
|  | DCHECK(CanBeTaggedPointer(access_info.field_representation())); | 
|  | FieldIndex field_index = access_info.field_index(); | 
|  | constant = JSObject::FastPropertyAt(holder, Representation::Tagged(), | 
|  | field_index); | 
|  | } | 
|  | DCHECK(constant->IsCallable()); | 
|  |  | 
|  | // Check that {constructor} is actually {receiver}. | 
|  | constructor = | 
|  | access_builder.BuildCheckValue(constructor, &effect, control, receiver); | 
|  |  | 
|  | // Monomorphic property access. | 
|  | access_builder.BuildCheckMaps(constructor, &effect, control, | 
|  | access_info.receiver_maps()); | 
|  |  | 
|  | // Create a nested frame state inside the current method's most-recent frame | 
|  | // state that will ensure that deopts that happen after this point will not | 
|  | // fallback to the last Checkpoint--which would completely re-execute the | 
|  | // instanceof logic--but rather create an activation of a version of the | 
|  | // ToBoolean stub that finishes the remaining work of instanceof and returns | 
|  | // to the caller without duplicating side-effects upon a lazy deopt. | 
|  | Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState( | 
|  | jsgraph(), Builtins::kToBooleanLazyDeoptContinuation, context, nullptr, | 
|  | 0, frame_state, ContinuationFrameStateMode::LAZY); | 
|  |  | 
|  | // Call the @@hasInstance handler. | 
|  | Node* target = jsgraph()->Constant(constant); | 
|  | node->InsertInput(graph()->zone(), 0, target); | 
|  | node->ReplaceInput(1, constructor); | 
|  | node->ReplaceInput(2, object); | 
|  | node->ReplaceInput(4, continuation_frame_state); | 
|  | node->ReplaceInput(5, effect); | 
|  | NodeProperties::ChangeOp( | 
|  | node, javascript()->Call(3, CallFrequency(), VectorSlotPair(), | 
|  | ConvertReceiverMode::kNotNullOrUndefined)); | 
|  |  | 
|  | // Rewire the value uses of {node} to ToBoolean conversion of the result. | 
|  | Node* value = graph()->NewNode(simplified()->ToBoolean(), node); | 
|  | for (Edge edge : node->use_edges()) { | 
|  | if (NodeProperties::IsValueEdge(edge) && edge.from() != value) { | 
|  | edge.UpdateTo(value); | 
|  | Revisit(edge.from()); | 
|  | } | 
|  | } | 
|  | return Changed(node); | 
|  | } | 
|  |  | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | JSNativeContextSpecialization::InferHasInPrototypeChainResult | 
|  | JSNativeContextSpecialization::InferHasInPrototypeChain( | 
|  | Node* receiver, Node* effect, Handle<HeapObject> prototype) { | 
|  | ZoneHandleSet<Map> receiver_maps; | 
|  | NodeProperties::InferReceiverMapsResult result = | 
|  | NodeProperties::InferReceiverMaps(isolate(), 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(isolate(), 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 JSNativeContextSpecialization::ReduceJSHasInPrototypeChain( | 
|  | Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode()); | 
|  | Node* value = NodeProperties::GetValueInput(node, 0); | 
|  | Node* prototype = NodeProperties::GetValueInput(node, 1); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  |  | 
|  | // Check if we can constant-fold the prototype chain walk | 
|  | // for the given {value} and the {prototype}. | 
|  | HeapObjectMatcher m(prototype); | 
|  | if (m.HasValue()) { | 
|  | InferHasInPrototypeChainResult result = | 
|  | InferHasInPrototypeChain(value, effect, m.Value()); | 
|  | if (result != kMayBeInPrototypeChain) { | 
|  | Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( | 
|  | Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); | 
|  | Node* constructor = NodeProperties::GetValueInput(node, 0); | 
|  | Node* object = NodeProperties::GetValueInput(node, 1); | 
|  |  | 
|  | // Check if the {constructor} is known at compile time. | 
|  | HeapObjectMatcher m(constructor); | 
|  | 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. | 
|  | Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value()); | 
|  | Handle<JSReceiver> bound_target_function(function->bound_target_function(), | 
|  | isolate()); | 
|  | NodeProperties::ReplaceValueInput(node, object, 0); | 
|  | NodeProperties::ReplaceValueInput( | 
|  | node, jsgraph()->HeapConstant(bound_target_function), 1); | 
|  | NodeProperties::ChangeOp(node, javascript()->InstanceOf(VectorSlotPair())); | 
|  | Reduction const reduction = ReduceJSInstanceOf(node); | 
|  | 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_prototype_slot() && | 
|  | 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); | 
|  |  | 
|  | MapRef initial_map = dependencies()->DependOnInitialMap( | 
|  | JSFunctionRef(js_heap_broker(), function)); | 
|  | Node* prototype = jsgraph()->Constant( | 
|  | handle(initial_map.object<Map>()->prototype(), isolate())); | 
|  |  | 
|  | // Lower the {node} to JSHasInPrototypeChain. | 
|  | NodeProperties::ReplaceValueInput(node, object, 0); | 
|  | NodeProperties::ReplaceValueInput(node, prototype, 1); | 
|  | NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); | 
|  | Reduction const reduction = ReduceJSHasInPrototypeChain(node); | 
|  | return reduction.Changed() ? reduction : Changed(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | // ES section #sec-promise-resolve | 
|  | Reduction JSNativeContextSpecialization::ReduceJSPromiseResolve(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSPromiseResolve, node->opcode()); | 
|  | Node* constructor = NodeProperties::GetValueInput(node, 0); | 
|  | Node* value = 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 the %Promise% function. | 
|  | HeapObjectMatcher m(constructor); | 
|  | if (!m.Is(handle(native_context().object<Context>()->promise_function(), | 
|  | isolate()))) | 
|  | return NoChange(); | 
|  |  | 
|  | // Check if we know something about the {value}. | 
|  | ZoneHandleSet<Map> value_maps; | 
|  | NodeProperties::InferReceiverMapsResult result = | 
|  | NodeProperties::InferReceiverMaps(isolate(), value, effect, &value_maps); | 
|  | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); | 
|  | DCHECK_NE(0, value_maps.size()); | 
|  |  | 
|  | // Check that the {value} cannot be a JSPromise. | 
|  | for (Handle<Map> const value_map : value_maps) { | 
|  | if (value_map->IsJSPromiseMap()) return NoChange(); | 
|  | } | 
|  |  | 
|  | // Create a %Promise% instance and resolve it with {value}. | 
|  | Node* promise = effect = | 
|  | graph()->NewNode(javascript()->CreatePromise(), context, effect); | 
|  | effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value, | 
|  | context, frame_state, effect, control); | 
|  | ReplaceWithValue(node, promise, effect, control); | 
|  | return Replace(promise); | 
|  | } | 
|  |  | 
|  | // ES section #sec-promise-resolve-functions | 
|  | Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode()); | 
|  | Node* promise = NodeProperties::GetValueInput(node, 0); | 
|  | Node* resolution = NodeProperties::GetValueInput(node, 1); | 
|  | Node* context = NodeProperties::GetContextInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Check if we know something about the {resolution}. | 
|  | ZoneHandleSet<Map> resolution_maps; | 
|  | NodeProperties::InferReceiverMapsResult result = | 
|  | NodeProperties::InferReceiverMaps(isolate(), resolution, effect, | 
|  | &resolution_maps); | 
|  | if (result != NodeProperties::kReliableReceiverMaps) return NoChange(); | 
|  | DCHECK_NE(0, resolution_maps.size()); | 
|  |  | 
|  | // Compute property access info for "then" on {resolution}. | 
|  | PropertyAccessInfo access_info; | 
|  | AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), | 
|  | native_context().object<Context>(), | 
|  | graph()->zone()); | 
|  | if (!access_info_factory.ComputePropertyAccessInfo( | 
|  | MapHandles(resolution_maps.begin(), resolution_maps.end()), | 
|  | factory()->then_string(), AccessMode::kLoad, &access_info)) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | // We can further optimize the case where {resolution} | 
|  | // definitely doesn't have a "then" property. | 
|  | if (!access_info.IsNotFound()) return NoChange(); | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  |  | 
|  | // Add proper dependencies on the {resolution}s [[Prototype]]s. | 
|  | Handle<JSObject> holder; | 
|  | if (access_info.holder().ToHandle(&holder)) { | 
|  | dependencies()->DependOnStablePrototypeChains( | 
|  | js_heap_broker(), native_context().object<Context>(), | 
|  | access_info.receiver_maps(), holder); | 
|  | } | 
|  |  | 
|  | // Simply fulfill the {promise} with the {resolution}. | 
|  | Node* value = effect = | 
|  | graph()->NewNode(javascript()->FulfillPromise(), promise, resolution, | 
|  | context, effect, control); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); | 
|  | ContextAccess const& access = ContextAccessOf(node->op()); | 
|  | // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native | 
|  | // context (if any), so we can constant-fold those fields, which is | 
|  | // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. | 
|  | if (access.index() == Context::NATIVE_CONTEXT_INDEX) { | 
|  | Node* value = jsgraph()->Constant(native_context()); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | FieldAccess ForPropertyCellValue(MachineRepresentation representation, | 
|  | Type type, MaybeHandle<Map> map, | 
|  | Handle<Name> name) { | 
|  | WriteBarrierKind kind = kFullWriteBarrier; | 
|  | if (representation == MachineRepresentation::kTaggedSigned) { | 
|  | kind = kNoWriteBarrier; | 
|  | } else if (representation == MachineRepresentation::kTaggedPointer) { | 
|  | kind = kPointerWriteBarrier; | 
|  | } | 
|  | MachineType r = MachineType::TypeForRepresentation(representation); | 
|  | FieldAccess access = { | 
|  | kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind}; | 
|  | return access; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceGlobalAccess( | 
|  | Node* node, Node* receiver, Node* value, Handle<Name> name, | 
|  | AccessMode access_mode, Node* index) { | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Lookup on the global object. We only deal with own data properties | 
|  | // of the global object here (represented as PropertyCell). | 
|  | LookupIterator it(isolate(), global_object(), name, LookupIterator::OWN); | 
|  | it.TryLookupCachedProperty(); | 
|  | if (it.state() != LookupIterator::DATA) return NoChange(); | 
|  | if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); | 
|  | Handle<PropertyCell> property_cell = it.GetPropertyCell(); | 
|  | PropertyDetails property_details = property_cell->property_details(); | 
|  | Handle<Object> property_cell_value(property_cell->value(), isolate()); | 
|  | PropertyCellType property_cell_type = property_details.cell_type(); | 
|  |  | 
|  | // We have additional constraints for stores. | 
|  | if (access_mode == AccessMode::kStore) { | 
|  | if (property_details.IsReadOnly()) { | 
|  | // Don't even bother trying to lower stores to read-only data properties. | 
|  | return NoChange(); | 
|  | } else if (property_cell_type == PropertyCellType::kUndefined) { | 
|  | // There's no fast-path for dealing with undefined property cells. | 
|  | return NoChange(); | 
|  | } else if (property_cell_type == PropertyCellType::kConstantType) { | 
|  | // There's also no fast-path to store to a global cell which pretended | 
|  | // to be stable, but is no longer stable now. | 
|  | if (property_cell_value->IsHeapObject() && | 
|  | !Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) { | 
|  | return NoChange(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ensure that {index} matches the specified {name} (if {index} is given). | 
|  | if (index != nullptr) { | 
|  | effect = BuildCheckEqualsName(name, index, effect, control); | 
|  | } | 
|  |  | 
|  | // Check if we have a {receiver} to validate. If so, we need to check that | 
|  | // the {receiver} is actually the JSGlobalProxy for the native context that | 
|  | // we are specializing to. | 
|  | if (receiver != nullptr) { | 
|  | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver, | 
|  | jsgraph()->HeapConstant(global_proxy())); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kReceiverNotAGlobalProxy), | 
|  | check, effect, control); | 
|  | } | 
|  |  | 
|  | if (access_mode == AccessMode::kLoad) { | 
|  | // Load from non-configurable, read-only data property on the global | 
|  | // object can be constant-folded, even without deoptimization support. | 
|  | if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { | 
|  | value = jsgraph()->Constant(property_cell_value); | 
|  | } else { | 
|  | // Record a code dependency on the cell if we can benefit from the | 
|  | // additional feedback, or the global property is configurable (i.e. | 
|  | // can be deleted or reconfigured to an accessor property). | 
|  | if (property_details.cell_type() != PropertyCellType::kMutable || | 
|  | property_details.IsConfigurable()) { | 
|  | dependencies()->DependOnGlobalProperty( | 
|  | PropertyCellRef(js_heap_broker(), property_cell)); | 
|  | } | 
|  |  | 
|  | // Load from constant/undefined global property can be constant-folded. | 
|  | if (property_details.cell_type() == PropertyCellType::kConstant || | 
|  | property_details.cell_type() == PropertyCellType::kUndefined) { | 
|  | value = jsgraph()->Constant(property_cell_value); | 
|  | } else { | 
|  | // Load from constant type cell can benefit from type feedback. | 
|  | MaybeHandle<Map> map; | 
|  | Type property_cell_value_type = Type::NonInternal(); | 
|  | MachineRepresentation representation = MachineRepresentation::kTagged; | 
|  | if (property_details.cell_type() == PropertyCellType::kConstantType) { | 
|  | // Compute proper type based on the current value in the cell. | 
|  | if (property_cell_value->IsSmi()) { | 
|  | property_cell_value_type = Type::SignedSmall(); | 
|  | representation = MachineRepresentation::kTaggedSigned; | 
|  | } else if (property_cell_value->IsNumber()) { | 
|  | property_cell_value_type = Type::Number(); | 
|  | representation = MachineRepresentation::kTaggedPointer; | 
|  | } else { | 
|  | Handle<Map> property_cell_value_map( | 
|  | Handle<HeapObject>::cast(property_cell_value)->map(), | 
|  | isolate()); | 
|  | property_cell_value_type = | 
|  | Type::For(js_heap_broker(), property_cell_value_map); | 
|  | representation = MachineRepresentation::kTaggedPointer; | 
|  |  | 
|  | // We can only use the property cell value map for map check | 
|  | // elimination if it's stable, i.e. the HeapObject wasn't | 
|  | // mutated without the cell state being updated. | 
|  | if (property_cell_value_map->is_stable()) { | 
|  | dependencies()->DependOnStableMap( | 
|  | MapRef(js_heap_broker(), property_cell_value_map)); | 
|  | map = property_cell_value_map; | 
|  | } | 
|  | } | 
|  | } | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->LoadField(ForPropertyCellValue( | 
|  | representation, property_cell_value_type, map, name)), | 
|  | jsgraph()->HeapConstant(property_cell), effect, control); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(AccessMode::kStore, access_mode); | 
|  | DCHECK(!property_details.IsReadOnly()); | 
|  | switch (property_details.cell_type()) { | 
|  | case PropertyCellType::kUndefined: { | 
|  | UNREACHABLE(); | 
|  | break; | 
|  | } | 
|  | case PropertyCellType::kConstant: { | 
|  | // Record a code dependency on the cell, and just deoptimize if the new | 
|  | // value doesn't match the previous value stored inside the cell. | 
|  | dependencies()->DependOnGlobalProperty( | 
|  | PropertyCellRef(js_heap_broker(), property_cell)); | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->ReferenceEqual(), value, | 
|  | jsgraph()->Constant(property_cell_value)); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kValueMismatch), check, | 
|  | effect, control); | 
|  | break; | 
|  | } | 
|  | case PropertyCellType::kConstantType: { | 
|  | // Record a code dependency on the cell, and just deoptimize if the new | 
|  | // values' type doesn't match the type of the previous value in the | 
|  | // cell. | 
|  | dependencies()->DependOnGlobalProperty( | 
|  | PropertyCellRef(js_heap_broker(), property_cell)); | 
|  | Type property_cell_value_type; | 
|  | MachineRepresentation representation = MachineRepresentation::kTagged; | 
|  | if (property_cell_value->IsHeapObject()) { | 
|  | // We cannot do anything if the {property_cell_value}s map is no | 
|  | // longer stable. | 
|  | Handle<Map> property_cell_value_map( | 
|  | Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); | 
|  | DCHECK(property_cell_value_map->is_stable()); | 
|  | dependencies()->DependOnStableMap( | 
|  | MapRef(js_heap_broker(), property_cell_value_map)); | 
|  |  | 
|  | // Check that the {value} is a HeapObject. | 
|  | value = effect = graph()->NewNode(simplified()->CheckHeapObject(), | 
|  | value, effect, control); | 
|  |  | 
|  | // Check {value} map against the {property_cell} map. | 
|  | effect = | 
|  | graph()->NewNode(simplified()->CheckMaps( | 
|  | CheckMapsFlag::kNone, | 
|  | ZoneHandleSet<Map>(property_cell_value_map)), | 
|  | value, effect, control); | 
|  | property_cell_value_type = Type::OtherInternal(); | 
|  | representation = MachineRepresentation::kTaggedPointer; | 
|  | } else { | 
|  | // Check that the {value} is a Smi. | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->CheckSmi(VectorSlotPair()), value, effect, control); | 
|  | property_cell_value_type = Type::SignedSmall(); | 
|  | representation = MachineRepresentation::kTaggedSigned; | 
|  | } | 
|  | effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue( | 
|  | representation, property_cell_value_type, | 
|  | MaybeHandle<Map>(), name)), | 
|  | jsgraph()->HeapConstant(property_cell), value, | 
|  | effect, control); | 
|  | break; | 
|  | } | 
|  | case PropertyCellType::kMutable: { | 
|  | // Record a code dependency on the cell, and just deoptimize if the | 
|  | // property ever becomes read-only. | 
|  | dependencies()->DependOnGlobalProperty( | 
|  | PropertyCellRef(js_heap_broker(), property_cell)); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->StoreField(ForPropertyCellValue( | 
|  | MachineRepresentation::kTagged, Type::NonInternal(), | 
|  | MaybeHandle<Map>(), name)), | 
|  | jsgraph()->HeapConstant(property_cell), value, effect, control); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); | 
|  | NameRef name(js_heap_broker(), LoadGlobalParametersOf(node->op()).name()); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  |  | 
|  | // Try to lookup the name on the script context table first (lexical scoping). | 
|  | base::Optional<ScriptContextTableRef::LookupResult> result = | 
|  | native_context().script_context_table().lookup(name); | 
|  | if (result) { | 
|  | ObjectRef contents = result->context.get(result->index); | 
|  | OddballType oddball_type = contents.oddball_type(); | 
|  | if (oddball_type == OddballType::kHole) { | 
|  | return NoChange(); | 
|  | } | 
|  | Node* context = jsgraph()->Constant(result->context); | 
|  | Node* value = effect = graph()->NewNode( | 
|  | javascript()->LoadContext(0, result->index, result->immutable), context, | 
|  | effect); | 
|  | ReplaceWithValue(node, value, effect); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | // Lookup the {name} on the global object instead. | 
|  | return ReduceGlobalAccess(node, nullptr, nullptr, name.object<Name>(), | 
|  | AccessMode::kLoad); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); | 
|  | NameRef name(js_heap_broker(), StoreGlobalParametersOf(node->op()).name()); | 
|  | Node* value = NodeProperties::GetValueInput(node, 0); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Try to lookup the name on the script context table first (lexical scoping). | 
|  | base::Optional<ScriptContextTableRef::LookupResult> result = | 
|  | native_context().script_context_table().lookup(name); | 
|  | if (result) { | 
|  | ObjectRef contents = result->context.get(result->index); | 
|  | OddballType oddball_type = contents.oddball_type(); | 
|  | if (oddball_type == OddballType::kHole || result->immutable) { | 
|  | return NoChange(); | 
|  | } | 
|  | Node* context = jsgraph()->Constant(result->context); | 
|  | effect = graph()->NewNode(javascript()->StoreContext(0, result->index), | 
|  | value, context, effect, control); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | // Lookup the {name} on the global object instead. | 
|  | return ReduceGlobalAccess(node, nullptr, value, name.object<Name>(), | 
|  | AccessMode::kStore); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceNamedAccess( | 
|  | Node* node, Node* value, MapHandles const& receiver_maps, Handle<Name> name, | 
|  | AccessMode access_mode, Node* index) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 
|  | node->opcode() == IrOpcode::kJSStoreNamed || | 
|  | node->opcode() == IrOpcode::kJSLoadProperty || | 
|  | node->opcode() == IrOpcode::kJSStoreProperty || | 
|  | node->opcode() == IrOpcode::kJSStoreNamedOwn); | 
|  | Node* receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* context = NodeProperties::GetContextInput(node); | 
|  | Node* frame_state = NodeProperties::GetFrameStateInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // 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.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. | 
|  | if (maybe_constructor->IsJSFunction() && | 
|  | JSFunction::cast(maybe_constructor)->native_context() == | 
|  | *native_context().object<Context>()) { | 
|  | return ReduceGlobalAccess(node, receiver, value, name, access_mode, | 
|  | index); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Compute property access infos for the receiver maps. | 
|  | AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), | 
|  | native_context().object<Context>(), | 
|  | graph()->zone()); | 
|  | ZoneVector<PropertyAccessInfo> access_infos(zone()); | 
|  | if (!access_info_factory.ComputePropertyAccessInfos( | 
|  | receiver_maps, name, access_mode, &access_infos)) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | // Nothing to do if we have no non-deprecated maps. | 
|  | if (access_infos.empty()) { | 
|  | return ReduceSoftDeoptimize( | 
|  | node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); | 
|  | } | 
|  |  | 
|  | // Ensure that {index} matches the specified {name} (if {index} is given). | 
|  | if (index != nullptr) { | 
|  | effect = BuildCheckEqualsName(name, index, effect, control); | 
|  | } | 
|  |  | 
|  | // Collect call nodes to rewire exception edges. | 
|  | ZoneVector<Node*> if_exception_nodes(zone()); | 
|  | ZoneVector<Node*>* if_exceptions = nullptr; | 
|  | Node* if_exception = nullptr; | 
|  | if (NodeProperties::IsExceptionalCall(node, &if_exception)) { | 
|  | if_exceptions = &if_exception_nodes; | 
|  | } | 
|  |  | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  |  | 
|  | // Check for the monomorphic cases. | 
|  | if (access_infos.size() == 1) { | 
|  | PropertyAccessInfo access_info = access_infos.front(); | 
|  | // Try to build string check or number check if possible. | 
|  | // Otherwise build a map check. | 
|  | if (!access_builder.TryBuildStringCheck(access_info.receiver_maps(), | 
|  | &receiver, &effect, control) && | 
|  | !access_builder.TryBuildNumberCheck(access_info.receiver_maps(), | 
|  | &receiver, &effect, control)) { | 
|  | if (HasNumberMaps(access_info.receiver_maps())) { | 
|  | // We need to also let Smi {receiver}s through in this case, so | 
|  | // we construct a diamond, guarded by the Sminess of the {receiver} | 
|  | // and if {receiver} is not a Smi just emit a sequence of map checks. | 
|  | Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | 
|  | Node* branch = graph()->NewNode(common()->Branch(), check, control); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue = effect; | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* efalse = effect; | 
|  | { | 
|  | access_builder.BuildCheckMaps(receiver, &efalse, if_false, | 
|  | access_info.receiver_maps()); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); | 
|  | } else { | 
|  | receiver = | 
|  | access_builder.BuildCheckHeapObject(receiver, &effect, control); | 
|  | access_builder.BuildCheckMaps(receiver, &effect, control, | 
|  | access_info.receiver_maps()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generate the actual property access. | 
|  | ValueEffectControl continuation = BuildPropertyAccess( | 
|  | receiver, value, context, frame_state, effect, control, name, | 
|  | if_exceptions, access_info, access_mode); | 
|  | value = continuation.value(); | 
|  | effect = continuation.effect(); | 
|  | control = continuation.control(); | 
|  | } else { | 
|  | // The final states for every polymorphic branch. We join them with | 
|  | // Merge+Phi+EffectPhi at the bottom. | 
|  | ZoneVector<Node*> values(zone()); | 
|  | ZoneVector<Node*> effects(zone()); | 
|  | ZoneVector<Node*> controls(zone()); | 
|  |  | 
|  | // Check if {receiver} may be a number. | 
|  | bool receiverissmi_possible = false; | 
|  | for (PropertyAccessInfo const& access_info : access_infos) { | 
|  | if (HasNumberMaps(access_info.receiver_maps())) { | 
|  | receiverissmi_possible = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ensure that {receiver} is a heap object. | 
|  | Node* receiverissmi_control = nullptr; | 
|  | Node* receiverissmi_effect = effect; | 
|  | if (receiverissmi_possible) { | 
|  | Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | 
|  | Node* branch = graph()->NewNode(common()->Branch(), check, control); | 
|  | control = graph()->NewNode(common()->IfFalse(), branch); | 
|  | receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); | 
|  | receiverissmi_effect = effect; | 
|  | } else { | 
|  | receiver = | 
|  | access_builder.BuildCheckHeapObject(receiver, &effect, control); | 
|  | } | 
|  |  | 
|  | // Generate code for the various different property access patterns. | 
|  | Node* fallthrough_control = control; | 
|  | for (size_t j = 0; j < access_infos.size(); ++j) { | 
|  | PropertyAccessInfo const& access_info = access_infos[j]; | 
|  | Node* this_value = value; | 
|  | Node* this_receiver = receiver; | 
|  | Node* this_effect = effect; | 
|  | Node* this_control = fallthrough_control; | 
|  |  | 
|  | // Perform map check on {receiver}. | 
|  | MapHandles const& receiver_maps = access_info.receiver_maps(); | 
|  | { | 
|  | // Whether to insert a dedicated MapGuard node into the | 
|  | // effect to be able to learn from the control flow. | 
|  | bool insert_map_guard = true; | 
|  |  | 
|  | // Check maps for the {receiver}s. | 
|  | if (j == access_infos.size() - 1) { | 
|  | // Last map check on the fallthrough control path, do a | 
|  | // conditional eager deoptimization exit here. | 
|  | access_builder.BuildCheckMaps(receiver, &this_effect, this_control, | 
|  | receiver_maps); | 
|  | fallthrough_control = nullptr; | 
|  |  | 
|  | // Don't insert a MapGuard in this case, as the CheckMaps | 
|  | // node already gives you all the information you need | 
|  | // along the effect chain. | 
|  | insert_map_guard = false; | 
|  | } else { | 
|  | // Explicitly branch on the {receiver_maps}. | 
|  | ZoneHandleSet<Map> maps; | 
|  | for (Handle<Map> map : receiver_maps) { | 
|  | maps.insert(map, graph()->zone()); | 
|  | } | 
|  | Node* check = this_effect = | 
|  | graph()->NewNode(simplified()->CompareMaps(maps), receiver, | 
|  | this_effect, this_control); | 
|  | Node* branch = | 
|  | graph()->NewNode(common()->Branch(), check, this_control); | 
|  | fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 
|  | this_control = graph()->NewNode(common()->IfTrue(), branch); | 
|  | } | 
|  |  | 
|  | // The Number case requires special treatment to also deal with Smis. | 
|  | if (HasNumberMaps(receiver_maps)) { | 
|  | // Join this check with the "receiver is smi" check above. | 
|  | DCHECK_NOT_NULL(receiverissmi_effect); | 
|  | DCHECK_NOT_NULL(receiverissmi_control); | 
|  | this_control = graph()->NewNode(common()->Merge(2), this_control, | 
|  | receiverissmi_control); | 
|  | this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect, | 
|  | receiverissmi_effect, this_control); | 
|  | receiverissmi_effect = receiverissmi_control = nullptr; | 
|  |  | 
|  | // The {receiver} can also be a Smi in this case, so | 
|  | // a MapGuard doesn't make sense for this at all. | 
|  | insert_map_guard = false; | 
|  | } | 
|  |  | 
|  | // Introduce a MapGuard to learn from this on the effect chain. | 
|  | if (insert_map_guard) { | 
|  | ZoneHandleSet<Map> maps; | 
|  | for (auto receiver_map : receiver_maps) { | 
|  | maps.insert(receiver_map, graph()->zone()); | 
|  | } | 
|  | this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver, | 
|  | this_effect, this_control); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generate the actual property access. | 
|  | ValueEffectControl continuation = BuildPropertyAccess( | 
|  | this_receiver, this_value, context, frame_state, this_effect, | 
|  | this_control, name, if_exceptions, access_info, access_mode); | 
|  | values.push_back(continuation.value()); | 
|  | effects.push_back(continuation.effect()); | 
|  | controls.push_back(continuation.control()); | 
|  | } | 
|  |  | 
|  | DCHECK_NULL(fallthrough_control); | 
|  |  | 
|  | // Generate the final merge point for all (polymorphic) branches. | 
|  | int const control_count = static_cast<int>(controls.size()); | 
|  | if (control_count == 0) { | 
|  | value = effect = control = jsgraph()->Dead(); | 
|  | } else if (control_count == 1) { | 
|  | value = values.front(); | 
|  | effect = effects.front(); | 
|  | control = controls.front(); | 
|  | } else { | 
|  | control = graph()->NewNode(common()->Merge(control_count), control_count, | 
|  | &controls.front()); | 
|  | values.push_back(control); | 
|  | value = graph()->NewNode( | 
|  | common()->Phi(MachineRepresentation::kTagged, control_count), | 
|  | control_count + 1, &values.front()); | 
|  | effects.push_back(control); | 
|  | effect = graph()->NewNode(common()->EffectPhi(control_count), | 
|  | control_count + 1, &effects.front()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Properly rewire IfException edges if {node} is inside a try-block. | 
|  | if (!if_exception_nodes.empty()) { | 
|  | DCHECK_NOT_NULL(if_exception); | 
|  | DCHECK_EQ(if_exceptions, &if_exception_nodes); | 
|  | int const if_exception_count = static_cast<int>(if_exceptions->size()); | 
|  | Node* merge = graph()->NewNode(common()->Merge(if_exception_count), | 
|  | if_exception_count, &if_exceptions->front()); | 
|  | if_exceptions->push_back(merge); | 
|  | Node* ephi = | 
|  | graph()->NewNode(common()->EffectPhi(if_exception_count), | 
|  | if_exception_count + 1, &if_exceptions->front()); | 
|  | Node* phi = graph()->NewNode( | 
|  | common()->Phi(MachineRepresentation::kTagged, if_exception_count), | 
|  | if_exception_count + 1, &if_exceptions->front()); | 
|  | ReplaceWithValue(if_exception, phi, ephi, merge); | 
|  | } | 
|  |  | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( | 
|  | Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name, | 
|  | AccessMode access_mode) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 
|  | node->opcode() == IrOpcode::kJSStoreNamed || | 
|  | node->opcode() == IrOpcode::kJSStoreNamedOwn); | 
|  | Node* const receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* const effect = NodeProperties::GetEffectInput(node); | 
|  |  | 
|  | // Check if we are accessing the current native contexts' global proxy. | 
|  | HeapObjectMatcher m(receiver); | 
|  | if (m.HasValue() && m.Value().is_identical_to(global_proxy())) { | 
|  | // Optimize accesses to the current native contexts' global proxy. | 
|  | return ReduceGlobalAccess(node, nullptr, value, name, access_mode); | 
|  | } | 
|  |  | 
|  | // Extract receiver maps from the IC using the {nexus}. | 
|  | MapHandles receiver_maps; | 
|  | if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { | 
|  | return NoChange(); | 
|  | } else if (receiver_maps.empty()) { | 
|  | if (flags() & kBailoutOnUninitialized) { | 
|  | return ReduceSoftDeoptimize( | 
|  | node, | 
|  | DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  | DCHECK(!nexus.IsUninitialized()); | 
|  |  | 
|  | // Try to lower the named access based on the {receiver_maps}. | 
|  | return ReduceNamedAccess(node, value, receiver_maps, name, access_mode); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); | 
|  | NamedAccess const& p = NamedAccessOf(node->op()); | 
|  | Node* const receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* const value = jsgraph()->Dead(); | 
|  |  | 
|  | // Check if we have a constant receiver. | 
|  | HeapObjectMatcher m(receiver); | 
|  | if (m.HasValue()) { | 
|  | if (m.Value()->IsJSFunction() && | 
|  | p.name().is_identical_to(factory()->prototype_string())) { | 
|  | // Optimize "prototype" property of functions. | 
|  | Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); | 
|  | if (!function->PrototypeRequiresRuntimeLookup()) { | 
|  | // 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}. | 
|  | JSFunction::EnsureHasInitialMap(function); | 
|  | dependencies()->DependOnInitialMap( | 
|  | JSFunctionRef(js_heap_broker(), function)); | 
|  | Handle<Object> prototype(function->prototype(), isolate()); | 
|  | Node* value = jsgraph()->Constant(prototype); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  | } else if (m.Value()->IsString() && | 
|  | p.name().is_identical_to(factory()->length_string())) { | 
|  | // Constant-fold "length" property on constant strings. | 
|  | Handle<String> string = Handle<String>::cast(m.Value()); | 
|  | Node* value = jsgraph()->Constant(string->length()); | 
|  | ReplaceWithValue(node, value); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Extract receiver maps from the load IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Try to lower the named access based on the {receiver_maps}. | 
|  | return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), | 
|  | AccessMode::kLoad); | 
|  | } | 
|  |  | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | 
|  | NamedAccess const& p = NamedAccessOf(node->op()); | 
|  | Node* const value = NodeProperties::GetValueInput(node, 1); | 
|  |  | 
|  | // Extract receiver maps from the store IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Try to lower the named access based on the {receiver_maps}. | 
|  | return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), | 
|  | AccessMode::kStore); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode()); | 
|  | StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op()); | 
|  | Node* const value = NodeProperties::GetValueInput(node, 1); | 
|  |  | 
|  | // Extract receiver maps from the IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Try to lower the creation of a named property based on the {receiver_maps}. | 
|  | return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), | 
|  | AccessMode::kStoreInLiteral); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceElementAccess( | 
|  | Node* node, Node* index, Node* value, MapHandles const& receiver_maps, | 
|  | AccessMode access_mode, KeyedAccessLoadMode load_mode, | 
|  | KeyedAccessStoreMode store_mode) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || | 
|  | node->opcode() == IrOpcode::kJSStoreProperty || | 
|  | node->opcode() == IrOpcode::kJSStoreInArrayLiteral); | 
|  | Node* receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* frame_state = NodeProperties::FindFrameStateBefore(node); | 
|  |  | 
|  | // Check for keyed access to strings. | 
|  | if (HasOnlyStringMaps(receiver_maps)) { | 
|  | // Strings are immutable in JavaScript. | 
|  | if (access_mode == AccessMode::kStore) return NoChange(); | 
|  |  | 
|  | // Ensure that the {receiver} is actually a String. | 
|  | receiver = effect = graph()->NewNode( | 
|  | simplified()->CheckString(VectorSlotPair()), receiver, effect, control); | 
|  |  | 
|  | // Determine the {receiver} length. | 
|  | Node* length = graph()->NewNode(simplified()->StringLength(), receiver); | 
|  |  | 
|  | // Load the single character string from {receiver} or yield undefined | 
|  | // if the {index} is out of bounds (depending on the {load_mode}). | 
|  | value = BuildIndexedStringLoad(receiver, index, length, &effect, &control, | 
|  | load_mode); | 
|  | } else { | 
|  | // Retrieve the native context from the given {node}. | 
|  | // Compute element access infos for the receiver maps. | 
|  | AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), | 
|  | native_context().object<Context>(), | 
|  | graph()->zone()); | 
|  | ZoneVector<ElementAccessInfo> access_infos(zone()); | 
|  | if (!access_info_factory.ComputeElementAccessInfos( | 
|  | receiver_maps, access_mode, &access_infos)) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | // Nothing to do if we have no non-deprecated maps. | 
|  | if (access_infos.empty()) { | 
|  | return ReduceSoftDeoptimize( | 
|  | node, | 
|  | DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); | 
|  | } | 
|  |  | 
|  | // For holey stores or growing stores, we need to check that the prototype | 
|  | // chain contains no setters for elements, and we need to guard those checks | 
|  | // via code dependencies on the relevant prototype maps. | 
|  | if (access_mode == AccessMode::kStore) { | 
|  | // TODO(turbofan): We could have a fast path here, that checks for the | 
|  | // common case of Array or Object prototype only and therefore avoids | 
|  | // the zone allocation of this vector. | 
|  | ZoneVector<Handle<Map>> prototype_maps(zone()); | 
|  | for (ElementAccessInfo const& access_info : access_infos) { | 
|  | for (Handle<Map> receiver_map : access_info.receiver_maps()) { | 
|  | // If the {receiver_map} has a prototype and it's elements backing | 
|  | // store is either holey, or we have a potentially growing store, | 
|  | // then we need to check that all prototypes have stable maps with | 
|  | // fast elements (and we need to guard against changes to that below). | 
|  | if (IsHoleyOrDictionaryElementsKind(receiver_map->elements_kind()) || | 
|  | IsGrowStoreMode(store_mode)) { | 
|  | // Make sure all prototypes are stable and have fast elements. | 
|  | for (Handle<Map> map = receiver_map;;) { | 
|  | Handle<Object> map_prototype(map->prototype(), isolate()); | 
|  | if (map_prototype->IsNull(isolate())) break; | 
|  | if (!map_prototype->IsJSObject()) return NoChange(); | 
|  | map = handle(Handle<JSObject>::cast(map_prototype)->map(), | 
|  | isolate()); | 
|  | if (!map->is_stable()) return NoChange(); | 
|  | if (!IsFastElementsKind(map->elements_kind())) return NoChange(); | 
|  | prototype_maps.push_back(map); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Install dependencies on the relevant prototype maps. | 
|  | for (Handle<Map> prototype_map : prototype_maps) { | 
|  | dependencies()->DependOnStableMap( | 
|  | MapRef(js_heap_broker(), prototype_map)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ensure that {receiver} is a heap object. | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  | receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control); | 
|  |  | 
|  | // Check for the monomorphic case. | 
|  | if (access_infos.size() == 1) { | 
|  | ElementAccessInfo access_info = access_infos.front(); | 
|  |  | 
|  | // Perform possible elements kind transitions. | 
|  | for (auto transition : access_info.transitions()) { | 
|  | Handle<Map> const transition_source = transition.first; | 
|  | Handle<Map> const transition_target = transition.second; | 
|  | effect = graph()->NewNode( | 
|  | simplified()->TransitionElementsKind(ElementsTransition( | 
|  | IsSimpleMapChangeTransition(transition_source->elements_kind(), | 
|  | transition_target->elements_kind()) | 
|  | ? ElementsTransition::kFastTransition | 
|  | : ElementsTransition::kSlowTransition, | 
|  | transition_source, transition_target)), | 
|  | receiver, effect, control); | 
|  | } | 
|  |  | 
|  | // TODO(turbofan): The effect/control linearization will not find a | 
|  | // FrameState after the StoreField or Call that is generated for the | 
|  | // elements kind transition above. This is because those operators | 
|  | // don't have the kNoWrite flag on it, even though they are not | 
|  | // observable by JavaScript. | 
|  | effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, | 
|  | control); | 
|  |  | 
|  | // Perform map check on the {receiver}. | 
|  | access_builder.BuildCheckMaps(receiver, &effect, control, | 
|  | access_info.receiver_maps()); | 
|  |  | 
|  | // Access the actual element. | 
|  | ValueEffectControl continuation = | 
|  | BuildElementAccess(receiver, index, value, effect, control, | 
|  | access_info, access_mode, load_mode, store_mode); | 
|  | value = continuation.value(); | 
|  | effect = continuation.effect(); | 
|  | control = continuation.control(); | 
|  | } else { | 
|  | // The final states for every polymorphic branch. We join them with | 
|  | // Merge+Phi+EffectPhi at the bottom. | 
|  | ZoneVector<Node*> values(zone()); | 
|  | ZoneVector<Node*> effects(zone()); | 
|  | ZoneVector<Node*> controls(zone()); | 
|  |  | 
|  | // Generate code for the various different element access patterns. | 
|  | Node* fallthrough_control = control; | 
|  | for (size_t j = 0; j < access_infos.size(); ++j) { | 
|  | ElementAccessInfo const& access_info = access_infos[j]; | 
|  | Node* this_receiver = receiver; | 
|  | Node* this_value = value; | 
|  | Node* this_index = index; | 
|  | Node* this_effect = effect; | 
|  | Node* this_control = fallthrough_control; | 
|  |  | 
|  | // Perform possible elements kind transitions. | 
|  | for (auto transition : access_info.transitions()) { | 
|  | Handle<Map> const transition_source = transition.first; | 
|  | Handle<Map> const transition_target = transition.second; | 
|  | this_effect = graph()->NewNode( | 
|  | simplified()->TransitionElementsKind( | 
|  | ElementsTransition(IsSimpleMapChangeTransition( | 
|  | transition_source->elements_kind(), | 
|  | transition_target->elements_kind()) | 
|  | ? ElementsTransition::kFastTransition | 
|  | : ElementsTransition::kSlowTransition, | 
|  | transition_source, transition_target)), | 
|  | receiver, this_effect, this_control); | 
|  | } | 
|  |  | 
|  | // Perform map check(s) on {receiver}. | 
|  | 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. | 
|  | access_builder.BuildCheckMaps(receiver, &this_effect, this_control, | 
|  | receiver_maps); | 
|  | fallthrough_control = nullptr; | 
|  | } else { | 
|  | // Explicitly branch on the {receiver_maps}. | 
|  | ZoneHandleSet<Map> maps; | 
|  | for (Handle<Map> map : receiver_maps) { | 
|  | maps.insert(map, graph()->zone()); | 
|  | } | 
|  | Node* check = this_effect = | 
|  | graph()->NewNode(simplified()->CompareMaps(maps), receiver, | 
|  | this_effect, fallthrough_control); | 
|  | Node* branch = | 
|  | graph()->NewNode(common()->Branch(), check, fallthrough_control); | 
|  | fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 
|  | this_control = graph()->NewNode(common()->IfTrue(), branch); | 
|  |  | 
|  | // Introduce a MapGuard to learn from this on the effect chain. | 
|  | this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver, | 
|  | this_effect, this_control); | 
|  | } | 
|  |  | 
|  | // Access the actual element. | 
|  | ValueEffectControl continuation = BuildElementAccess( | 
|  | this_receiver, this_index, this_value, this_effect, this_control, | 
|  | access_info, access_mode, load_mode, store_mode); | 
|  | values.push_back(continuation.value()); | 
|  | effects.push_back(continuation.effect()); | 
|  | controls.push_back(continuation.control()); | 
|  | } | 
|  |  | 
|  | DCHECK_NULL(fallthrough_control); | 
|  |  | 
|  | // Generate the final merge point for all (polymorphic) branches. | 
|  | int const control_count = static_cast<int>(controls.size()); | 
|  | if (control_count == 0) { | 
|  | value = effect = control = jsgraph()->Dead(); | 
|  | } else if (control_count == 1) { | 
|  | value = values.front(); | 
|  | effect = effects.front(); | 
|  | control = controls.front(); | 
|  | } else { | 
|  | control = graph()->NewNode(common()->Merge(control_count), | 
|  | control_count, &controls.front()); | 
|  | values.push_back(control); | 
|  | value = graph()->NewNode( | 
|  | common()->Phi(MachineRepresentation::kTagged, control_count), | 
|  | control_count + 1, &values.front()); | 
|  | effects.push_back(control); | 
|  | effect = graph()->NewNode(common()->EffectPhi(control_count), | 
|  | control_count + 1, &effects.front()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceKeyedAccess( | 
|  | Node* node, Node* index, Node* value, FeedbackNexus const& nexus, | 
|  | AccessMode access_mode, KeyedAccessLoadMode load_mode, | 
|  | KeyedAccessStoreMode store_mode) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || | 
|  | node->opcode() == IrOpcode::kJSStoreProperty); | 
|  | Node* receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Optimize the case where we load from a constant {receiver}. | 
|  | if (access_mode == AccessMode::kLoad) { | 
|  | HeapObjectMatcher mreceiver(receiver); | 
|  | if (mreceiver.HasValue() && !mreceiver.Value()->IsTheHole(isolate()) && | 
|  | !mreceiver.Value()->IsNullOrUndefined(isolate())) { | 
|  | // Check whether we're accessing a known element on the {receiver} | 
|  | // that is non-configurable, non-writable (i.e. the {receiver} was | 
|  | // frozen using Object.freeze). | 
|  | NumberMatcher mindex(index); | 
|  | if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32 - 1.0)) { | 
|  | LookupIterator it(isolate(), mreceiver.Value(), | 
|  | static_cast<uint32_t>(mindex.Value()), | 
|  | LookupIterator::OWN); | 
|  | if (it.state() == LookupIterator::DATA) { | 
|  | if (it.IsReadOnly() && !it.IsConfigurable()) { | 
|  | // We can safely constant-fold the {index} access to {receiver}, | 
|  | // since the element is non-configurable, non-writable and thus | 
|  | // cannot change anymore. | 
|  | value = jsgraph()->Constant(it.GetDataValue()); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | // Check if the {receiver} is a known constant with a copy-on-write | 
|  | // backing store, and whether {index} is within the appropriate | 
|  | // bounds. In that case we can constant-fold the access and only | 
|  | // check that the {elements} didn't change. This is sufficient as | 
|  | // the backing store of a copy-on-write JSArray is defensively copied | 
|  | // whenever the length or the elements (might) change. | 
|  | // | 
|  | // What's interesting here is that we don't need to map check the | 
|  | // {receiver}, since JSArray's will always have their elements in | 
|  | // the backing store. | 
|  | if (mreceiver.Value()->IsJSArray()) { | 
|  | Handle<JSArray> array = Handle<JSArray>::cast(mreceiver.Value()); | 
|  | if (array->elements()->IsCowArray()) { | 
|  | Node* elements = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | 
|  | receiver, effect, control); | 
|  | Handle<FixedArray> array_elements( | 
|  | FixedArray::cast(array->elements()), isolate()); | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->ReferenceEqual(), elements, | 
|  | jsgraph()->HeapConstant(array_elements)); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf( | 
|  | DeoptimizeReason::kCowArrayElementsChanged), | 
|  | check, effect, control); | 
|  | value = jsgraph()->Constant(it.GetDataValue()); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // For constant Strings we can eagerly strength-reduce the keyed | 
|  | // accesses using the known length, which doesn't change. | 
|  | if (mreceiver.Value()->IsString()) { | 
|  | Handle<String> string = Handle<String>::cast(mreceiver.Value()); | 
|  |  | 
|  | // We can only assume that the {index} is a valid array index if the IC | 
|  | // is in element access mode and not MEGAMORPHIC, otherwise there's no | 
|  | // guard for the bounds check below. | 
|  | if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) { | 
|  | // Ensure that {index} is less than {receiver} length. | 
|  | Node* length = jsgraph()->Constant(string->length()); | 
|  |  | 
|  | // Load the single character string from {receiver} or yield undefined | 
|  | // if the {index} is out of bounds (depending on the {load_mode}). | 
|  | value = BuildIndexedStringLoad(receiver, index, length, &effect, | 
|  | &control, load_mode); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Extract receiver maps from the {nexus}. | 
|  | MapHandles receiver_maps; | 
|  | if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { | 
|  | return NoChange(); | 
|  | } else if (receiver_maps.empty()) { | 
|  | if (flags() & kBailoutOnUninitialized) { | 
|  | return ReduceSoftDeoptimize( | 
|  | node, | 
|  | DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  | DCHECK(!nexus.IsUninitialized()); | 
|  |  | 
|  | // Optimize access for constant {index}. | 
|  | HeapObjectMatcher mindex(index); | 
|  | if (mindex.HasValue() && mindex.Value()->IsPrimitive()) { | 
|  | // Keyed access requires a ToPropertyKey on the {index} first before | 
|  | // looking up the property on the object (see ES6 section 12.3.2.1). | 
|  | // We can only do this for non-observable ToPropertyKey invocations, | 
|  | // so we limit the constant indices to primitives at this point. | 
|  | Handle<Name> name; | 
|  | if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) { | 
|  | uint32_t array_index; | 
|  | if (name->AsArrayIndex(&array_index)) { | 
|  | // Use the constant array index. | 
|  | index = jsgraph()->Constant(static_cast<double>(array_index)); | 
|  | } else { | 
|  | name = factory()->InternalizeName(name); | 
|  | return ReduceNamedAccess(node, value, receiver_maps, name, access_mode); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check if we have feedback for a named access. | 
|  | if (Name* name = nexus.FindFirstName()) { | 
|  | return ReduceNamedAccess(node, value, receiver_maps, | 
|  | handle(name, isolate()), access_mode, index); | 
|  | } else if (nexus.GetKeyType() != ELEMENT) { | 
|  | // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume | 
|  | // that the {index} is a valid array index, thus we just let the IC continue | 
|  | // to deal with this load/store. | 
|  | return NoChange(); | 
|  | } else if (nexus.ic_state() == MEGAMORPHIC) { | 
|  | // The KeyedLoad/StoreIC uses the MEGAMORPHIC state to guard the assumption | 
|  | // that a numeric {index} is within the valid bounds for {receiver}, i.e. | 
|  | // it transitions to MEGAMORPHIC once it sees an out-of-bounds access. Thus | 
|  | // we cannot continue here if the IC state is MEGAMORPHIC. | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | // Try to lower the element access based on the {receiver_maps}. | 
|  | return ReduceElementAccess(node, index, value, receiver_maps, access_mode, | 
|  | load_mode, store_mode); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize( | 
|  | Node* node, DeoptimizeReason reason) { | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* frame_state = NodeProperties::FindFrameStateBefore(node); | 
|  | Node* deoptimize = graph()->NewNode( | 
|  | common()->Deoptimize(DeoptimizeKind::kSoft, reason, VectorSlotPair()), | 
|  | frame_state, effect, control); | 
|  | // TODO(bmeurer): This should be on the AdvancedReducer somehow. | 
|  | NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | 
|  | Revisit(graph()->end()); | 
|  | node->TrimInputCount(0); | 
|  | NodeProperties::ChangeOp(node, common()->Dead()); | 
|  | return Changed(node); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); | 
|  | PropertyAccess const& p = PropertyAccessOf(node->op()); | 
|  | Node* receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* name = NodeProperties::GetValueInput(node, 1); | 
|  | Node* value = jsgraph()->Dead(); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // We can optimize a property load if it's being used inside a for..in, | 
|  | // so for code like this: | 
|  | // | 
|  | //   for (name in receiver) { | 
|  | //     value = receiver[name]; | 
|  | //     ... | 
|  | //   } | 
|  | // | 
|  | // If the for..in is in fast-mode, we know that the {receiver} has {name} | 
|  | // as own property, otherwise the enumeration wouldn't include it. The graph | 
|  | // constructed by the BytecodeGraphBuilder in this case looks like this: | 
|  |  | 
|  | // receiver | 
|  | //  ^    ^ | 
|  | //  |    | | 
|  | //  |    +-+ | 
|  | //  |      | | 
|  | //  |   JSToObject | 
|  | //  |      ^ | 
|  | //  |      | | 
|  | //  |      | | 
|  | //  |  JSForInNext | 
|  | //  |      ^ | 
|  | //  |      | | 
|  | //  +----+ | | 
|  | //       | | | 
|  | //       | | | 
|  | //   JSLoadProperty | 
|  |  | 
|  | // If the for..in has only seen maps with enum cache consisting of keys | 
|  | // and indices so far, we can turn the {JSLoadProperty} into a map check | 
|  | // on the {receiver} and then just load the field value dynamically via | 
|  | // the {LoadFieldByIndex} operator. The map check is only necessary when | 
|  | // TurboFan cannot prove that there is no observable side effect between | 
|  | // the {JSForInNext} and the {JSLoadProperty} node. | 
|  | // | 
|  | // Also note that it's safe to look through the {JSToObject}, since the | 
|  | // [[Get]] operation does an implicit ToObject anyway, and these operations | 
|  | // are not observable. | 
|  | if (name->opcode() == IrOpcode::kJSForInNext) { | 
|  | ForInMode const mode = ForInModeOf(name->op()); | 
|  | if (mode == ForInMode::kUseEnumCacheKeysAndIndices) { | 
|  | Node* object = NodeProperties::GetValueInput(name, 0); | 
|  | Node* enumerator = NodeProperties::GetValueInput(name, 2); | 
|  | Node* index = NodeProperties::GetValueInput(name, 3); | 
|  | if (object->opcode() == IrOpcode::kJSToObject) { | 
|  | object = NodeProperties::GetValueInput(object, 0); | 
|  | } | 
|  | if (object == receiver) { | 
|  | // No need to repeat the map check if we can prove that there's no | 
|  | // observable side effect between {effect} and {name]. | 
|  | if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { | 
|  | // Check that the {receiver} map is still valid. | 
|  | Node* receiver_map = effect = | 
|  | graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 
|  | receiver, effect, control); | 
|  | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), | 
|  | receiver_map, enumerator); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect, | 
|  | control); | 
|  | } | 
|  |  | 
|  | // Load the enum cache indices from the {cache_type}. | 
|  | Node* descriptor_array = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForMapDescriptors()), | 
|  | enumerator, effect, control); | 
|  | Node* enum_cache = effect = | 
|  | graph()->NewNode(simplified()->LoadField( | 
|  | AccessBuilder::ForDescriptorArrayEnumCache()), | 
|  | descriptor_array, effect, control); | 
|  | Node* enum_indices = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()), | 
|  | enum_cache, effect, control); | 
|  |  | 
|  | // Ensure that the {enum_indices} are valid. | 
|  | Node* check = graph()->NewNode( | 
|  | simplified()->BooleanNot(), | 
|  | graph()->NewNode(simplified()->ReferenceEqual(), enum_indices, | 
|  | jsgraph()->EmptyFixedArrayConstant())); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kWrongEnumIndices), check, | 
|  | effect, control); | 
|  |  | 
|  | // Determine the index from the {enum_indices}. | 
|  | index = effect = graph()->NewNode( | 
|  | simplified()->LoadElement( | 
|  | AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)), | 
|  | enum_indices, index, effect, control); | 
|  |  | 
|  | // Load the actual field value. | 
|  | Node* value = effect = graph()->NewNode( | 
|  | simplified()->LoadFieldByIndex(), receiver, index, effect, control); | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Extract receiver maps from the keyed load IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Extract the keyed access load mode from the keyed load IC. | 
|  | KeyedAccessLoadMode load_mode = nexus.GetKeyedAccessLoadMode(); | 
|  |  | 
|  | // Try to lower the keyed access based on the {nexus}. | 
|  | return ReduceKeyedAccess(node, name, value, nexus, AccessMode::kLoad, | 
|  | load_mode, STANDARD_STORE); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); | 
|  | PropertyAccess const& p = PropertyAccessOf(node->op()); | 
|  | Node* const index = NodeProperties::GetValueInput(node, 1); | 
|  | Node* const value = NodeProperties::GetValueInput(node, 2); | 
|  |  | 
|  | // Extract receiver maps from the keyed store IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Extract the keyed access store mode from the keyed store IC. | 
|  | KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); | 
|  |  | 
|  | // Try to lower the keyed access based on the {nexus}. | 
|  | return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, | 
|  | STANDARD_LOAD, store_mode); | 
|  | } | 
|  |  | 
|  | Node* JSNativeContextSpecialization::InlinePropertyGetterCall( | 
|  | Node* receiver, Node* context, Node* frame_state, Node** effect, | 
|  | Node** control, ZoneVector<Node*>* if_exceptions, | 
|  | PropertyAccessInfo const& access_info) { | 
|  | Node* target = jsgraph()->Constant(access_info.constant()); | 
|  | FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op()); | 
|  | Handle<SharedFunctionInfo> shared_info = | 
|  | frame_info.shared_info().ToHandleChecked(); | 
|  | // Introduce the call to the getter function. | 
|  | Node* value; | 
|  | if (access_info.constant()->IsJSFunction()) { | 
|  | value = *effect = *control = graph()->NewNode( | 
|  | jsgraph()->javascript()->Call(2, CallFrequency(), VectorSlotPair(), | 
|  | ConvertReceiverMode::kNotNullOrUndefined), | 
|  | target, receiver, context, frame_state, *effect, *control); | 
|  | } else { | 
|  | DCHECK(access_info.constant()->IsFunctionTemplateInfo()); | 
|  | Handle<FunctionTemplateInfo> function_template_info( | 
|  | Handle<FunctionTemplateInfo>::cast(access_info.constant())); | 
|  | DCHECK(!function_template_info->call_code()->IsUndefined(isolate())); | 
|  | Node* holder = | 
|  | access_info.holder().is_null() | 
|  | ? receiver | 
|  | : jsgraph()->Constant(access_info.holder().ToHandleChecked()); | 
|  | value = InlineApiCall(receiver, holder, frame_state, nullptr, effect, | 
|  | control, shared_info, function_template_info); | 
|  | } | 
|  | // Remember to rewire the IfException edge if this is inside a try-block. | 
|  | if (if_exceptions != nullptr) { | 
|  | // Create the appropriate IfException/IfSuccess projections. | 
|  | Node* const if_exception = | 
|  | graph()->NewNode(common()->IfException(), *control, *effect); | 
|  | Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control); | 
|  | if_exceptions->push_back(if_exception); | 
|  | *control = if_success; | 
|  | } | 
|  | return value; | 
|  | } | 
|  |  | 
|  | void JSNativeContextSpecialization::InlinePropertySetterCall( | 
|  | Node* receiver, Node* value, Node* context, Node* frame_state, | 
|  | Node** effect, Node** control, ZoneVector<Node*>* if_exceptions, | 
|  | PropertyAccessInfo const& access_info) { | 
|  | Node* target = jsgraph()->Constant(access_info.constant()); | 
|  | FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op()); | 
|  | Handle<SharedFunctionInfo> shared_info = | 
|  | frame_info.shared_info().ToHandleChecked(); | 
|  | // Introduce the call to the setter function. | 
|  | if (access_info.constant()->IsJSFunction()) { | 
|  | *effect = *control = graph()->NewNode( | 
|  | jsgraph()->javascript()->Call(3, CallFrequency(), VectorSlotPair(), | 
|  | ConvertReceiverMode::kNotNullOrUndefined), | 
|  | target, receiver, value, context, frame_state, *effect, *control); | 
|  | } else { | 
|  | DCHECK(access_info.constant()->IsFunctionTemplateInfo()); | 
|  | Handle<FunctionTemplateInfo> function_template_info( | 
|  | Handle<FunctionTemplateInfo>::cast(access_info.constant())); | 
|  | DCHECK(!function_template_info->call_code()->IsUndefined(isolate())); | 
|  | Node* holder = | 
|  | access_info.holder().is_null() | 
|  | ? receiver | 
|  | : jsgraph()->Constant(access_info.holder().ToHandleChecked()); | 
|  | InlineApiCall(receiver, holder, frame_state, value, effect, control, | 
|  | shared_info, function_template_info); | 
|  | } | 
|  | // Remember to rewire the IfException edge if this is inside a try-block. | 
|  | if (if_exceptions != nullptr) { | 
|  | // Create the appropriate IfException/IfSuccess projections. | 
|  | Node* const if_exception = | 
|  | graph()->NewNode(common()->IfException(), *control, *effect); | 
|  | Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control); | 
|  | if_exceptions->push_back(if_exception); | 
|  | *control = if_success; | 
|  | } | 
|  | } | 
|  |  | 
|  | Node* JSNativeContextSpecialization::InlineApiCall( | 
|  | Node* receiver, Node* holder, Node* frame_state, Node* value, Node** effect, | 
|  | Node** control, Handle<SharedFunctionInfo> shared_info, | 
|  | Handle<FunctionTemplateInfo> function_template_info) { | 
|  | Handle<CallHandlerInfo> call_handler_info = handle( | 
|  | CallHandlerInfo::cast(function_template_info->call_code()), isolate()); | 
|  | Handle<Object> call_data_object(call_handler_info->data(), isolate()); | 
|  |  | 
|  | // Only setters have a value. | 
|  | int const argc = value == nullptr ? 0 : 1; | 
|  | // The stub always expects the receiver as the first param on the stack. | 
|  | Callable call_api_callback = CodeFactory::CallApiCallback(isolate(), argc); | 
|  | CallInterfaceDescriptor call_interface_descriptor = | 
|  | call_api_callback.descriptor(); | 
|  | auto call_descriptor = Linkage::GetStubCallDescriptor( | 
|  | graph()->zone(), call_interface_descriptor, | 
|  | call_interface_descriptor.GetStackParameterCount() + argc + | 
|  | 1 /* implicit receiver */, | 
|  | CallDescriptor::kNeedsFrameState); | 
|  |  | 
|  | Node* data = jsgraph()->Constant(call_data_object); | 
|  | ApiFunction function(v8::ToCData<Address>(call_handler_info->callback())); | 
|  | Node* function_reference = | 
|  | graph()->NewNode(common()->ExternalConstant(ExternalReference::Create( | 
|  | &function, ExternalReference::DIRECT_API_CALL))); | 
|  | Node* code = jsgraph()->HeapConstant(call_api_callback.code()); | 
|  |  | 
|  | // Add CallApiCallbackStub's register argument as well. | 
|  | Node* context = jsgraph()->Constant(native_context()); | 
|  | Node* inputs[10] = {code,    context, data, holder, function_reference, | 
|  | receiver}; | 
|  | int index = 6 + argc; | 
|  | inputs[index++] = frame_state; | 
|  | inputs[index++] = *effect; | 
|  | inputs[index++] = *control; | 
|  | // This needs to stay here because of the edge case described in | 
|  | // http://crbug.com/675648. | 
|  | if (value != nullptr) { | 
|  | inputs[6] = value; | 
|  | } | 
|  |  | 
|  | return *effect = *control = | 
|  | graph()->NewNode(common()->Call(call_descriptor), index, inputs); | 
|  | } | 
|  |  | 
|  | JSNativeContextSpecialization::ValueEffectControl | 
|  | JSNativeContextSpecialization::BuildPropertyLoad( | 
|  | Node* receiver, Node* context, Node* frame_state, Node* effect, | 
|  | Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, | 
|  | PropertyAccessInfo const& access_info) { | 
|  | // Determine actual holder and perform prototype chain checks. | 
|  | Handle<JSObject> holder; | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  | if (access_info.holder().ToHandle(&holder)) { | 
|  | dependencies()->DependOnStablePrototypeChains( | 
|  | js_heap_broker(), native_context().object<Context>(), | 
|  | access_info.receiver_maps(), holder); | 
|  | } | 
|  |  | 
|  | // Generate the actual property access. | 
|  | Node* value; | 
|  | if (access_info.IsNotFound()) { | 
|  | value = jsgraph()->UndefinedConstant(); | 
|  | } else if (access_info.IsDataConstant()) { | 
|  | DCHECK(!FLAG_track_constant_fields); | 
|  | value = jsgraph()->Constant(access_info.constant()); | 
|  | } else if (access_info.IsAccessorConstant()) { | 
|  | value = InlinePropertyGetterCall(receiver, context, frame_state, &effect, | 
|  | &control, if_exceptions, access_info); | 
|  | } else if (access_info.IsModuleExport()) { | 
|  | Node* cell = jsgraph()->Constant(access_info.export_cell()); | 
|  | value = effect = | 
|  | graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()), | 
|  | cell, effect, control); | 
|  | } else { | 
|  | DCHECK(access_info.IsDataField() || access_info.IsDataConstantField()); | 
|  | value = access_builder.BuildLoadDataField(name, access_info, receiver, | 
|  | &effect, &control); | 
|  | } | 
|  |  | 
|  | return ValueEffectControl(value, effect, control); | 
|  | } | 
|  |  | 
|  | JSNativeContextSpecialization::ValueEffectControl | 
|  | JSNativeContextSpecialization::BuildPropertyAccess( | 
|  | Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, | 
|  | Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, | 
|  | PropertyAccessInfo const& access_info, AccessMode access_mode) { | 
|  | switch (access_mode) { | 
|  | case AccessMode::kLoad: | 
|  | return BuildPropertyLoad(receiver, context, frame_state, effect, control, | 
|  | name, if_exceptions, access_info); | 
|  | case AccessMode::kStore: | 
|  | case AccessMode::kStoreInLiteral: | 
|  | return BuildPropertyStore(receiver, value, context, frame_state, effect, | 
|  | control, name, if_exceptions, access_info, | 
|  | access_mode); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return ValueEffectControl(); | 
|  | } | 
|  |  | 
|  | JSNativeContextSpecialization::ValueEffectControl | 
|  | JSNativeContextSpecialization::BuildPropertyStore( | 
|  | Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, | 
|  | Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, | 
|  | PropertyAccessInfo const& access_info, AccessMode access_mode) { | 
|  | // Determine actual holder and perform prototype chain checks. | 
|  | Handle<JSObject> holder; | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  | if (access_info.holder().ToHandle(&holder)) { | 
|  | DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); | 
|  | dependencies()->DependOnStablePrototypeChains( | 
|  | js_heap_broker(), native_context().object<Context>(), | 
|  | access_info.receiver_maps(), holder); | 
|  | } | 
|  |  | 
|  | DCHECK(!access_info.IsNotFound()); | 
|  |  | 
|  | // Generate the actual property access. | 
|  | if (access_info.IsDataConstant()) { | 
|  | DCHECK(!FLAG_track_constant_fields); | 
|  | Node* constant_value = jsgraph()->Constant(access_info.constant()); | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->ReferenceEqual(), value, constant_value); | 
|  | effect = | 
|  | graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue), | 
|  | check, effect, control); | 
|  | value = constant_value; | 
|  | } else if (access_info.IsAccessorConstant()) { | 
|  | InlinePropertySetterCall(receiver, value, context, frame_state, &effect, | 
|  | &control, if_exceptions, access_info); | 
|  | } else { | 
|  | DCHECK(access_info.IsDataField() || access_info.IsDataConstantField()); | 
|  | FieldIndex const field_index = access_info.field_index(); | 
|  | Type const field_type = access_info.field_type(); | 
|  | MachineRepresentation const field_representation = | 
|  | access_info.field_representation(); | 
|  | Node* storage = receiver; | 
|  | if (!field_index.is_inobject()) { | 
|  | storage = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()), | 
|  | storage, effect, control); | 
|  | } | 
|  | FieldAccess field_access = { | 
|  | kTaggedBase, | 
|  | field_index.offset(), | 
|  | name, | 
|  | MaybeHandle<Map>(), | 
|  | field_type, | 
|  | MachineType::TypeForRepresentation(field_representation), | 
|  | kFullWriteBarrier}; | 
|  | bool store_to_constant_field = FLAG_track_constant_fields && | 
|  | (access_mode == AccessMode::kStore) && | 
|  | access_info.IsDataConstantField(); | 
|  |  | 
|  | DCHECK(access_mode == AccessMode::kStore || | 
|  | access_mode == AccessMode::kStoreInLiteral); | 
|  | switch (field_representation) { | 
|  | case MachineRepresentation::kFloat64: { | 
|  | value = effect = | 
|  | graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), value, | 
|  | effect, control); | 
|  | if (!field_index.is_inobject() || field_index.is_hidden_field() || | 
|  | !FLAG_unbox_double_fields) { | 
|  | if (access_info.HasTransitionMap()) { | 
|  | // Allocate a MutableHeapNumber for the new property. | 
|  | AllocationBuilder a(jsgraph(), effect, control); | 
|  | a.Allocate(HeapNumber::kSize, NOT_TENURED, Type::OtherInternal()); | 
|  | a.Store(AccessBuilder::ForMap(), | 
|  | factory()->mutable_heap_number_map()); | 
|  | a.Store(AccessBuilder::ForHeapNumberValue(), value); | 
|  | value = effect = a.Finish(); | 
|  |  | 
|  | field_access.type = Type::Any(); | 
|  | field_access.machine_type = MachineType::TaggedPointer(); | 
|  | field_access.write_barrier_kind = kPointerWriteBarrier; | 
|  | } else { | 
|  | // We just store directly to the MutableHeapNumber. | 
|  | FieldAccess const storage_access = {kTaggedBase, | 
|  | field_index.offset(), | 
|  | name, | 
|  | MaybeHandle<Map>(), | 
|  | Type::OtherInternal(), | 
|  | MachineType::TaggedPointer(), | 
|  | kPointerWriteBarrier}; | 
|  | storage = effect = | 
|  | graph()->NewNode(simplified()->LoadField(storage_access), | 
|  | storage, effect, control); | 
|  | field_access.offset = HeapNumber::kValueOffset; | 
|  | field_access.name = MaybeHandle<Name>(); | 
|  | field_access.machine_type = MachineType::Float64(); | 
|  | } | 
|  | } | 
|  | if (store_to_constant_field) { | 
|  | DCHECK(!access_info.HasTransitionMap()); | 
|  | // If the field is constant check that the value we are going | 
|  | // to store matches current value. | 
|  | Node* current_value = effect = graph()->NewNode( | 
|  | simplified()->LoadField(field_access), storage, effect, control); | 
|  |  | 
|  | Node* check = graph()->NewNode(simplified()->NumberEqual(), | 
|  | current_value, value); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, | 
|  | effect, control); | 
|  | return ValueEffectControl(value, effect, control); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case MachineRepresentation::kTaggedSigned: | 
|  | case MachineRepresentation::kTaggedPointer: | 
|  | case MachineRepresentation::kTagged: | 
|  | if (store_to_constant_field) { | 
|  | DCHECK(!access_info.HasTransitionMap()); | 
|  | // If the field is constant check that the value we are going | 
|  | // to store matches current value. | 
|  | Node* current_value = effect = graph()->NewNode( | 
|  | simplified()->LoadField(field_access), storage, effect, control); | 
|  |  | 
|  | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), | 
|  | current_value, value); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, | 
|  | effect, control); | 
|  | return ValueEffectControl(value, effect, control); | 
|  | } | 
|  |  | 
|  | if (field_representation == MachineRepresentation::kTaggedSigned) { | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->CheckSmi(VectorSlotPair()), value, effect, control); | 
|  | field_access.write_barrier_kind = kNoWriteBarrier; | 
|  |  | 
|  | } else if (field_representation == | 
|  | MachineRepresentation::kTaggedPointer) { | 
|  | // Ensure that {value} is a HeapObject. | 
|  | value = access_builder.BuildCheckHeapObject(value, &effect, control); | 
|  | Handle<Map> field_map; | 
|  | if (access_info.field_map().ToHandle(&field_map)) { | 
|  | // Emit a map check for the value. | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckMaps(CheckMapsFlag::kNone, | 
|  | ZoneHandleSet<Map>(field_map)), | 
|  | value, effect, control); | 
|  | } | 
|  | field_access.write_barrier_kind = kPointerWriteBarrier; | 
|  |  | 
|  | } else { | 
|  | DCHECK_EQ(MachineRepresentation::kTagged, field_representation); | 
|  | } | 
|  | break; | 
|  | case MachineRepresentation::kNone: | 
|  | case MachineRepresentation::kBit: | 
|  | case MachineRepresentation::kWord8: | 
|  | case MachineRepresentation::kWord16: | 
|  | case MachineRepresentation::kWord32: | 
|  | case MachineRepresentation::kWord64: | 
|  | case MachineRepresentation::kFloat32: | 
|  | case MachineRepresentation::kSimd128: | 
|  | UNREACHABLE(); | 
|  | break; | 
|  | } | 
|  | // Check if we need to perform a transitioning store. | 
|  | Handle<Map> transition_map; | 
|  | if (access_info.transition_map().ToHandle(&transition_map)) { | 
|  | // Check if we need to grow the properties backing store | 
|  | // with this transitioning store. | 
|  | Handle<Map> original_map(Map::cast(transition_map->GetBackPointer()), | 
|  | isolate()); | 
|  | if (original_map->UnusedPropertyFields() == 0) { | 
|  | DCHECK(!field_index.is_inobject()); | 
|  |  | 
|  | // Reallocate the properties {storage}. | 
|  | storage = effect = BuildExtendPropertiesBackingStore( | 
|  | original_map, storage, effect, control); | 
|  |  | 
|  | // Perform the actual store. | 
|  | effect = graph()->NewNode(simplified()->StoreField(field_access), | 
|  | storage, value, effect, control); | 
|  |  | 
|  | // Atomically switch to the new properties below. | 
|  | field_access = AccessBuilder::ForJSObjectPropertiesOrHash(); | 
|  | value = storage; | 
|  | storage = receiver; | 
|  | } | 
|  | effect = graph()->NewNode( | 
|  | common()->BeginRegion(RegionObservability::kObservable), effect); | 
|  | effect = graph()->NewNode( | 
|  | simplified()->StoreField(AccessBuilder::ForMap()), receiver, | 
|  | jsgraph()->Constant(transition_map), effect, control); | 
|  | effect = graph()->NewNode(simplified()->StoreField(field_access), storage, | 
|  | value, effect, control); | 
|  | effect = graph()->NewNode(common()->FinishRegion(), | 
|  | jsgraph()->UndefinedConstant(), effect); | 
|  | } else { | 
|  | // Regular non-transitioning field store. | 
|  | effect = graph()->NewNode(simplified()->StoreField(field_access), storage, | 
|  | value, effect, control); | 
|  | } | 
|  | } | 
|  |  | 
|  | return ValueEffectControl(value, effect, control); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral( | 
|  | Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreDataPropertyInLiteral, node->opcode()); | 
|  |  | 
|  | FeedbackParameter const& p = FeedbackParameterOf(node->op()); | 
|  |  | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  |  | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  | if (nexus.IsUninitialized()) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | if (nexus.ic_state() == MEGAMORPHIC) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | DCHECK_EQ(MONOMORPHIC, nexus.ic_state()); | 
|  |  | 
|  | Map* map = nexus.FindFirstMap(); | 
|  | if (map == nullptr) { | 
|  | // Maps are weakly held in the type feedback vector, we may not have one. | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Handle<Map> receiver_map(map, isolate()); | 
|  | if (!Map::TryUpdate(isolate(), receiver_map).ToHandle(&receiver_map)) | 
|  | return NoChange(); | 
|  |  | 
|  | Handle<Name> cached_name = handle( | 
|  | Name::cast(nexus.GetFeedbackExtra()->ToStrongHeapObject()), isolate()); | 
|  |  | 
|  | PropertyAccessInfo access_info; | 
|  | AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), | 
|  | native_context().object<Context>(), | 
|  | graph()->zone()); | 
|  | if (!access_info_factory.ComputePropertyAccessInfo( | 
|  | receiver_map, cached_name, AccessMode::kStoreInLiteral, | 
|  | &access_info)) { | 
|  | return NoChange(); | 
|  | } | 
|  |  | 
|  | Node* receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  |  | 
|  | // Monomorphic property access. | 
|  | PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), | 
|  | dependencies()); | 
|  | receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control); | 
|  | access_builder.BuildCheckMaps(receiver, &effect, control, | 
|  | access_info.receiver_maps()); | 
|  |  | 
|  | // Ensure that {name} matches the cached name. | 
|  | Node* name = NodeProperties::GetValueInput(node, 1); | 
|  | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), name, | 
|  | jsgraph()->HeapConstant(cached_name)); | 
|  | effect = graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongName), | 
|  | check, effect, control); | 
|  |  | 
|  | Node* value = NodeProperties::GetValueInput(node, 2); | 
|  | Node* context = NodeProperties::GetContextInput(node); | 
|  | Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node); | 
|  |  | 
|  | // Generate the actual property access. | 
|  | ValueEffectControl continuation = BuildPropertyAccess( | 
|  | receiver, value, context, frame_state_lazy, effect, control, cached_name, | 
|  | nullptr, access_info, AccessMode::kStoreInLiteral); | 
|  | value = continuation.value(); | 
|  | effect = continuation.effect(); | 
|  | control = continuation.control(); | 
|  |  | 
|  | ReplaceWithValue(node, value, effect, control); | 
|  | return Replace(value); | 
|  | } | 
|  |  | 
|  | Reduction JSNativeContextSpecialization::ReduceJSStoreInArrayLiteral( | 
|  | Node* node) { | 
|  | DCHECK_EQ(IrOpcode::kJSStoreInArrayLiteral, node->opcode()); | 
|  | FeedbackParameter const& p = FeedbackParameterOf(node->op()); | 
|  | Node* const receiver = NodeProperties::GetValueInput(node, 0); | 
|  | Node* const index = NodeProperties::GetValueInput(node, 1); | 
|  | Node* const value = NodeProperties::GetValueInput(node, 2); | 
|  | Node* const effect = NodeProperties::GetEffectInput(node); | 
|  |  | 
|  | // Extract receiver maps from the keyed store IC using the FeedbackNexus. | 
|  | if (!p.feedback().IsValid()) return NoChange(); | 
|  | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); | 
|  |  | 
|  | // Extract the keyed access store mode from the keyed store IC. | 
|  | KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); | 
|  |  | 
|  | // Extract receiver maps from the {nexus}. | 
|  | MapHandles receiver_maps; | 
|  | if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { | 
|  | return NoChange(); | 
|  | } else if (receiver_maps.empty()) { | 
|  | if (flags() & kBailoutOnUninitialized) { | 
|  | return ReduceSoftDeoptimize( | 
|  | node, | 
|  | DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); | 
|  | } | 
|  | return NoChange(); | 
|  | } | 
|  | DCHECK(!nexus.IsUninitialized()); | 
|  | DCHECK_EQ(ELEMENT, nexus.GetKeyType()); | 
|  |  | 
|  | if (nexus.ic_state() == MEGAMORPHIC) return NoChange(); | 
|  |  | 
|  | // Try to lower the element access based on the {receiver_maps}. | 
|  | return ReduceElementAccess(node, index, value, receiver_maps, | 
|  | AccessMode::kStoreInLiteral, STANDARD_LOAD, | 
|  | store_mode); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) { | 
|  | switch (kind) { | 
|  | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ | 
|  | case TYPE##_ELEMENTS:                           \ | 
|  | return kExternal##Type##Array; | 
|  | TYPED_ARRAYS(TYPED_ARRAY_CASE) | 
|  | #undef TYPED_ARRAY_CASE | 
|  | default: | 
|  | break; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | JSNativeContextSpecialization::ValueEffectControl | 
|  | JSNativeContextSpecialization::BuildElementAccess( | 
|  | Node* receiver, Node* index, Node* value, Node* effect, Node* control, | 
|  | ElementAccessInfo const& access_info, AccessMode access_mode, | 
|  | KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode) { | 
|  |  | 
|  | // 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(); | 
|  | MapHandles const& receiver_maps = access_info.receiver_maps(); | 
|  |  | 
|  | if (IsFixedTypedArrayElementsKind(elements_kind)) { | 
|  | Node* buffer; | 
|  | Node* length; | 
|  | Node* base_pointer; | 
|  | Node* external_pointer; | 
|  |  | 
|  | // Check if we can constant-fold information about the {receiver} (i.e. | 
|  | // for asm.js-like code patterns). | 
|  | HeapObjectMatcher m(receiver); | 
|  | if (m.HasValue() && m.Value()->IsJSTypedArray()) { | 
|  | Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(m.Value()); | 
|  |  | 
|  | // Determine the {receiver}s (known) length. | 
|  | length = | 
|  | jsgraph()->Constant(static_cast<double>(typed_array->length_value())); | 
|  |  | 
|  | // Check if the {receiver}s buffer was neutered. | 
|  | buffer = jsgraph()->HeapConstant(typed_array->GetBuffer()); | 
|  |  | 
|  | // Load the (known) base and external pointer for the {receiver}. The | 
|  | // {external_pointer} might be invalid if the {buffer} was neutered, so | 
|  | // we need to make sure that any access is properly guarded. | 
|  | base_pointer = jsgraph()->ZeroConstant(); | 
|  | external_pointer = jsgraph()->PointerConstant( | 
|  | FixedTypedArrayBase::cast(typed_array->elements()) | 
|  | ->external_pointer()); | 
|  | } else { | 
|  | // Load the {receiver}s length. | 
|  | length = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), | 
|  | receiver, effect, control); | 
|  |  | 
|  | // Load the buffer for the {receiver}. | 
|  | buffer = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), | 
|  | receiver, effect, control); | 
|  |  | 
|  | // Load the elements for the {receiver}. | 
|  | Node* elements = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | 
|  | receiver, effect, control); | 
|  |  | 
|  | // Load the base pointer for the {receiver}. This will always be Smi | 
|  | // zero unless we allow on-heap TypedArrays, which is only the case | 
|  | // for Chrome. Node and Electron both set this limit to 0. Setting | 
|  | // the base to Smi zero here allows the EffectControlLinearizer to | 
|  | // optimize away the tricky part of the access later. | 
|  | if (V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP == 0) { | 
|  | base_pointer = jsgraph()->ZeroConstant(); | 
|  | } else { | 
|  | base_pointer = effect = graph()->NewNode( | 
|  | simplified()->LoadField( | 
|  | AccessBuilder::ForFixedTypedArrayBaseBasePointer()), | 
|  | elements, effect, control); | 
|  | } | 
|  |  | 
|  | // Load the external pointer for the {receiver}s {elements}. | 
|  | external_pointer = effect = graph()->NewNode( | 
|  | simplified()->LoadField( | 
|  | AccessBuilder::ForFixedTypedArrayBaseExternalPointer()), | 
|  | elements, effect, control); | 
|  | } | 
|  |  | 
|  | // See if we can skip the neutering check. | 
|  | if (isolate()->IsArrayBufferNeuteringIntact()) { | 
|  | // Add a code dependency so we are deoptimized in case an ArrayBuffer | 
|  | // gets neutered. | 
|  | dependencies()->DependOnProtector(PropertyCellRef( | 
|  | js_heap_broker(), factory()->array_buffer_neutering_protector())); | 
|  | } else { | 
|  | // Default to zero if the {receiver}s buffer was neutered. | 
|  | Node* check = effect = graph()->NewNode( | 
|  | simplified()->ArrayBufferWasNeutered(), buffer, effect, control); | 
|  | length = graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), | 
|  | check, jsgraph()->ZeroConstant(), length); | 
|  | } | 
|  |  | 
|  | if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS || | 
|  | store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { | 
|  | // Only check that the {index} is in SignedSmall range. We do the actual | 
|  | // bounds check below and just skip the property access if it's out of | 
|  | // bounds for the {receiver}. | 
|  | index = effect = graph()->NewNode( | 
|  | simplified()->CheckSmi(VectorSlotPair()), index, effect, control); | 
|  |  | 
|  | // Cast the {index} to Unsigned32 range, so that the bounds checks | 
|  | // below are performed on unsigned values, which means that all the | 
|  | // Negative32 values are treated as out-of-bounds. | 
|  | index = graph()->NewNode(simplified()->NumberToUint32(), index); | 
|  | } else { | 
|  | // Check that the {index} is in the valid range for the {receiver}. | 
|  | index = effect = | 
|  | graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | length, effect, control); | 
|  | } | 
|  |  | 
|  | // Access the actual element. | 
|  | ExternalArrayType external_array_type = | 
|  | GetArrayTypeFromElementsKind(elements_kind); | 
|  | switch (access_mode) { | 
|  | case AccessMode::kLoad: { | 
|  | // Check if we can return undefined for out-of-bounds loads. | 
|  | if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS) { | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
|  | Node* branch = graph()->NewNode( | 
|  | common()->Branch(BranchHint::kTrue, | 
|  | IsSafetyCheck::kCriticalSafetyCheck), | 
|  | check, control); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue = effect; | 
|  | Node* vtrue; | 
|  | { | 
|  | // Perform the actual load | 
|  | vtrue = etrue = graph()->NewNode( | 
|  | simplified()->LoadTypedElement(external_array_type), buffer, | 
|  | base_pointer, external_pointer, index, etrue, if_true); | 
|  | } | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* efalse = effect; | 
|  | Node* vfalse; | 
|  | { | 
|  | // Materialize undefined for out-of-bounds loads. | 
|  | vfalse = jsgraph()->UndefinedConstant(); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); | 
|  | value = | 
|  | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
|  | vtrue, vfalse, control); | 
|  | } else { | 
|  | // Perform the actual load. | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->LoadTypedElement(external_array_type), buffer, | 
|  | base_pointer, external_pointer, index, effect, control); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case AccessMode::kStoreInLiteral: | 
|  | UNREACHABLE(); | 
|  | break; | 
|  | case AccessMode::kStore: { | 
|  | // Ensure that the {value} is actually a Number or an Oddball, | 
|  | // and truncate it to a Number appropriately. | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->SpeculativeToNumber( | 
|  | NumberOperationHint::kNumberOrOddball, VectorSlotPair()), | 
|  | value, effect, control); | 
|  |  | 
|  | // Introduce the appropriate truncation for {value}. Currently we | 
|  | // only need to do this for ClamedUint8Array {receiver}s, as the | 
|  | // other truncations are implicit in the StoreTypedElement, but we | 
|  | // might want to change that at some point. | 
|  | if (external_array_type == kExternalUint8ClampedArray) { | 
|  | value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value); | 
|  | } | 
|  |  | 
|  | // Check if we can skip the out-of-bounds store. | 
|  | if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
|  | Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 
|  | check, control); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue = effect; | 
|  | { | 
|  | // Perform the actual store. | 
|  | etrue = graph()->NewNode( | 
|  | simplified()->StoreTypedElement(external_array_type), buffer, | 
|  | base_pointer, external_pointer, index, value, etrue, if_true); | 
|  | } | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* efalse = effect; | 
|  | { | 
|  | // Just ignore the out-of-bounds write. | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); | 
|  | } else { | 
|  | // Perform the actual store | 
|  | effect = graph()->NewNode( | 
|  | simplified()->StoreTypedElement(external_array_type), buffer, | 
|  | base_pointer, external_pointer, index, value, effect, control); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // Load the elements for the {receiver}. | 
|  | Node* elements = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, | 
|  | effect, control); | 
|  |  | 
|  | // Don't try to store to a copy-on-write backing store (unless supported by | 
|  | // the store mode). | 
|  | if (access_mode == AccessMode::kStore && | 
|  | IsSmiOrObjectElementsKind(elements_kind) && | 
|  | !IsCOWHandlingStoreMode(store_mode)) { | 
|  | effect = graph()->NewNode( | 
|  | simplified()->CheckMaps( | 
|  | CheckMapsFlag::kNone, | 
|  | ZoneHandleSet<Map>(factory()->fixed_array_map())), | 
|  | elements, effect, control); | 
|  | } | 
|  |  | 
|  | // Check if the {receiver} is a JSArray. | 
|  | bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps); | 
|  |  | 
|  | // Load the length of the {receiver}. | 
|  | Node* length = effect = | 
|  | receiver_is_jsarray | 
|  | ? graph()->NewNode( | 
|  | simplified()->LoadField( | 
|  | AccessBuilder::ForJSArrayLength(elements_kind)), | 
|  | receiver, effect, control) | 
|  | : graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), | 
|  | elements, effect, control); | 
|  |  | 
|  | // Check if we might need to grow the {elements} backing store. | 
|  | if (IsGrowStoreMode(store_mode)) { | 
|  | // For growing stores we validate the {index} below. | 
|  | DCHECK(access_mode == AccessMode::kStore || | 
|  | access_mode == AccessMode::kStoreInLiteral); | 
|  | } else if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && | 
|  | CanTreatHoleAsUndefined(receiver_maps)) { | 
|  | // Check that the {index} is a valid array index, we do the actual | 
|  | // bounds check below and just skip the store below if it's out of | 
|  | // bounds for the {receiver}. | 
|  | index = effect = graph()->NewNode( | 
|  | simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | jsgraph()->Constant(Smi::kMaxValue), effect, control); | 
|  | } else { | 
|  | // Check that the {index} is in the valid range for the {receiver}. | 
|  | index = effect = | 
|  | graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | length, effect, control); | 
|  | } | 
|  |  | 
|  | // Compute the element access. | 
|  | Type element_type = Type::NonInternal(); | 
|  | MachineType element_machine_type = MachineType::AnyTagged(); | 
|  | if (IsDoubleElementsKind(elements_kind)) { | 
|  | element_type = Type::Number(); | 
|  | element_machine_type = MachineType::Float64(); | 
|  | } else if (IsSmiElementsKind(elements_kind)) { | 
|  | element_type = Type::SignedSmall(); | 
|  | element_machine_type = MachineType::TaggedSigned(); | 
|  | } | 
|  | ElementAccess element_access = { | 
|  | kTaggedBase,       FixedArray::kHeaderSize, | 
|  | element_type,      element_machine_type, | 
|  | kFullWriteBarrier, LoadSensitivity::kCritical}; | 
|  |  | 
|  | // Access the actual element. | 
|  | if (access_mode == AccessMode::kLoad) { | 
|  | // Compute the real element access type, which includes the hole in case | 
|  | // of holey backing stores. | 
|  | if (IsHoleyElementsKind(elements_kind)) { | 
|  | element_access.type = | 
|  | Type::Union(element_type, Type::Hole(), graph()->zone()); | 
|  | } | 
|  | if (elements_kind == HOLEY_ELEMENTS || | 
|  | elements_kind == HOLEY_SMI_ELEMENTS) { | 
|  | element_access.machine_type = MachineType::AnyTagged(); | 
|  | } | 
|  |  | 
|  | // Check if we can return undefined for out-of-bounds loads. | 
|  | if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && | 
|  | CanTreatHoleAsUndefined(receiver_maps)) { | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
|  | Node* branch = graph()->NewNode( | 
|  | common()->Branch(BranchHint::kTrue, | 
|  | IsSafetyCheck::kCriticalSafetyCheck), | 
|  | check, control); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue = effect; | 
|  | Node* vtrue; | 
|  | { | 
|  | // Perform the actual load | 
|  | vtrue = etrue = | 
|  | graph()->NewNode(simplified()->LoadElement(element_access), | 
|  | elements, index, etrue, if_true); | 
|  |  | 
|  | // Handle loading from holey backing stores correctly, by either | 
|  | // mapping the hole to undefined if possible, or deoptimizing | 
|  | // otherwise. | 
|  | if (elements_kind == HOLEY_ELEMENTS || | 
|  | elements_kind == HOLEY_SMI_ELEMENTS) { | 
|  | // Turn the hole into undefined. | 
|  | vtrue = graph()->NewNode( | 
|  | simplified()->ConvertTaggedHoleToUndefined(), vtrue); | 
|  | } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { | 
|  | // Return the signaling NaN hole directly if all uses are | 
|  | // truncating. | 
|  | vtrue = etrue = | 
|  | graph()->NewNode(simplified()->CheckFloat64Hole( | 
|  | CheckFloat64HoleMode::kAllowReturnHole), | 
|  | vtrue, etrue, if_true); | 
|  | } | 
|  | } | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* efalse = effect; | 
|  | Node* vfalse; | 
|  | { | 
|  | // Materialize undefined for out-of-bounds loads. | 
|  | vfalse = jsgraph()->UndefinedConstant(); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); | 
|  | value = | 
|  | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
|  | vtrue, vfalse, control); | 
|  | } else { | 
|  | // Perform the actual load. | 
|  | value = effect = | 
|  | graph()->NewNode(simplified()->LoadElement(element_access), | 
|  | elements, index, effect, control); | 
|  |  | 
|  | // Handle loading from holey backing stores correctly, by either mapping | 
|  | // the hole to undefined if possible, or deoptimizing otherwise. | 
|  | if (elements_kind == HOLEY_ELEMENTS || | 
|  | elements_kind == HOLEY_SMI_ELEMENTS) { | 
|  | // Check if we are allowed to turn the hole into undefined. | 
|  | if (CanTreatHoleAsUndefined(receiver_maps)) { | 
|  | // Turn the hole into undefined. | 
|  | value = graph()->NewNode( | 
|  | simplified()->ConvertTaggedHoleToUndefined(), value); | 
|  | } else { | 
|  | // Bailout if we see the hole. | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->CheckNotTaggedHole(), value, effect, control); | 
|  | } | 
|  | } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { | 
|  | // Perform the hole check on the result. | 
|  | CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole; | 
|  | // Check if we are allowed to return the hole directly. | 
|  | if (CanTreatHoleAsUndefined(receiver_maps)) { | 
|  | // Return the signaling NaN hole directly if all uses are | 
|  | // truncating. | 
|  | mode = CheckFloat64HoleMode::kAllowReturnHole; | 
|  | } | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->CheckFloat64Hole(mode), value, effect, control); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | DCHECK(access_mode == AccessMode::kStore || | 
|  | access_mode == AccessMode::kStoreInLiteral); | 
|  | if (IsSmiElementsKind(elements_kind)) { | 
|  | value = effect = graph()->NewNode( | 
|  | simplified()->CheckSmi(VectorSlotPair()), value, effect, control); | 
|  | } else if (IsDoubleElementsKind(elements_kind)) { | 
|  | value = effect = | 
|  | graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), value, | 
|  | effect, control); | 
|  | // Make sure we do not store signalling NaNs into double arrays. | 
|  | value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); | 
|  | } | 
|  |  | 
|  | // Ensure that copy-on-write backing store is writable. | 
|  | if (IsSmiOrObjectElementsKind(elements_kind) && | 
|  | store_mode == STORE_NO_TRANSITION_HANDLE_COW) { | 
|  | elements = effect = | 
|  | graph()->NewNode(simplified()->EnsureWritableFastElements(), | 
|  | receiver, elements, effect, control); | 
|  | } else if (IsGrowStoreMode(store_mode)) { | 
|  | // Determine the length of the {elements} backing store. | 
|  | Node* elements_length = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), | 
|  | elements, effect, control); | 
|  |  | 
|  | // Validate the {index} depending on holeyness: | 
|  | // | 
|  | // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements} | 
|  | // backing store capacity plus the maximum allowed gap, as otherwise | 
|  | // the (potential) backing store growth would normalize and thus | 
|  | // the elements kind of the {receiver} would change to slow mode. | 
|  | // | 
|  | // For PACKED_*_ELEMENTS the {index} must be within the range | 
|  | // [0,length+1[ to be valid. In case {index} equals {length}, | 
|  | // the {receiver} will be extended, but kept packed. | 
|  | Node* limit = | 
|  | IsHoleyElementsKind(elements_kind) | 
|  | ? graph()->NewNode(simplified()->NumberAdd(), elements_length, | 
|  | jsgraph()->Constant(JSObject::kMaxGap)) | 
|  | : graph()->NewNode(simplified()->NumberAdd(), length, | 
|  | jsgraph()->OneConstant()); | 
|  | index = effect = | 
|  | graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | limit, effect, control); | 
|  |  | 
|  | // Grow {elements} backing store if necessary. | 
|  | GrowFastElementsMode mode = | 
|  | IsDoubleElementsKind(elements_kind) | 
|  | ? GrowFastElementsMode::kDoubleElements | 
|  | : GrowFastElementsMode::kSmiOrObjectElements; | 
|  | elements = effect = graph()->NewNode( | 
|  | simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), | 
|  | receiver, elements, index, elements_length, effect, control); | 
|  |  | 
|  | // If we didn't grow {elements}, it might still be COW, in which case we | 
|  | // copy it now. | 
|  | if (IsSmiOrObjectElementsKind(elements_kind) && | 
|  | store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW) { | 
|  | elements = effect = | 
|  | graph()->NewNode(simplified()->EnsureWritableFastElements(), | 
|  | receiver, elements, effect, control); | 
|  | } | 
|  |  | 
|  | // Also update the "length" property if {receiver} is a JSArray. | 
|  | if (receiver_is_jsarray) { | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
|  | Node* branch = graph()->NewNode(common()->Branch(), check, control); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue = effect; | 
|  | { | 
|  | // We don't need to do anything, the {index} is within | 
|  | // the valid bounds for the JSArray {receiver}. | 
|  | } | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* efalse = effect; | 
|  | { | 
|  | // Update the JSArray::length field. Since this is observable, | 
|  | // there must be no other check after this. | 
|  | Node* new_length = graph()->NewNode( | 
|  | simplified()->NumberAdd(), index, jsgraph()->OneConstant()); | 
|  | efalse = graph()->NewNode( | 
|  | simplified()->StoreField( | 
|  | AccessBuilder::ForJSArrayLength(elements_kind)), | 
|  | receiver, new_length, efalse, if_false); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Perform the actual element access. | 
|  | effect = graph()->NewNode(simplified()->StoreElement(element_access), | 
|  | elements, index, value, effect, control); | 
|  | } | 
|  | } | 
|  |  | 
|  | return ValueEffectControl(value, effect, control); | 
|  | } | 
|  |  | 
|  | Node* JSNativeContextSpecialization::BuildIndexedStringLoad( | 
|  | Node* receiver, Node* index, Node* length, Node** effect, Node** control, | 
|  | KeyedAccessLoadMode load_mode) { | 
|  | if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && | 
|  | isolate()->IsNoElementsProtectorIntact()) { | 
|  | dependencies()->DependOnProtector( | 
|  | PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); | 
|  |  | 
|  | // Ensure that the {index} is a valid String length. | 
|  | index = *effect = graph()->NewNode( | 
|  | simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | jsgraph()->Constant(String::kMaxLength), *effect, *control); | 
|  |  | 
|  | // Load the single character string from {receiver} or yield | 
|  | // undefined if the {index} is not within the valid bounds. | 
|  | Node* check = | 
|  | graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
|  | Node* branch = | 
|  | graph()->NewNode(common()->Branch(BranchHint::kTrue, | 
|  | IsSafetyCheck::kCriticalSafetyCheck), | 
|  | check, *control); | 
|  |  | 
|  | Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); | 
|  |  | 
|  | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | 
|  | Node* etrue; | 
|  | Node* vtrue = etrue = | 
|  | graph()->NewNode(simplified()->StringCharCodeAt(), receiver, | 
|  | masked_index, *effect, if_true); | 
|  | vtrue = graph()->NewNode(simplified()->StringFromSingleCharCode(), vtrue); | 
|  |  | 
|  | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 
|  | Node* vfalse = jsgraph()->UndefinedConstant(); | 
|  |  | 
|  | *control = graph()->NewNode(common()->Merge(2), if_true, if_false); | 
|  | *effect = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue, *effect, *control); | 
|  | return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
|  | vtrue, vfalse, *control); | 
|  | } else { | 
|  | // Ensure that {index} is less than {receiver} length. | 
|  | index = *effect = | 
|  | graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index, | 
|  | length, *effect, *control); | 
|  |  | 
|  | Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); | 
|  |  | 
|  | // Return the character from the {receiver} as single character string. | 
|  | Node* value = *effect = | 
|  | graph()->NewNode(simplified()->StringCharCodeAt(), receiver, | 
|  | masked_index, *effect, *control); | 
|  | value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value); | 
|  | return value; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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->UnusedPropertyFields()); | 
|  | // Compute the length of the old {properties} and the new properties. | 
|  | int length = map->NextFreePropertyIndex() - map->GetInObjectProperties(); | 
|  | int new_length = length + JSObject::kFieldsAdded; | 
|  | // Collect the field values from the {properties}. | 
|  | ZoneVector<Node*> values(zone()); | 
|  | values.reserve(new_length); | 
|  | for (int i = 0; i < length; ++i) { | 
|  | Node* value = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)), | 
|  | properties, effect, control); | 
|  | values.push_back(value); | 
|  | } | 
|  | // Initialize the new fields to undefined. | 
|  | for (int i = 0; i < JSObject::kFieldsAdded; ++i) { | 
|  | values.push_back(jsgraph()->UndefinedConstant()); | 
|  | } | 
|  |  | 
|  | // Compute new length and hash. | 
|  | Node* hash; | 
|  | if (length == 0) { | 
|  | hash = graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kTaggedSigned), | 
|  | graph()->NewNode(simplified()->ObjectIsSmi(), properties), properties, | 
|  | jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel)); | 
|  | hash = effect = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), | 
|  | hash, effect, control); | 
|  | hash = | 
|  | graph()->NewNode(simplified()->NumberShiftLeft(), hash, | 
|  | jsgraph()->Constant(PropertyArray::HashField::kShift)); | 
|  | } else { | 
|  | hash = effect = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()), | 
|  | properties, effect, control); | 
|  | hash = | 
|  | graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, | 
|  | jsgraph()->Constant(PropertyArray::HashField::kMask)); | 
|  | } | 
|  | Node* new_length_and_hash = graph()->NewNode( | 
|  | simplified()->NumberBitwiseOr(), jsgraph()->Constant(new_length), hash); | 
|  | // TDOO(jarin): Fix the typer to infer tighter bound for NumberBitwiseOr. | 
|  | new_length_and_hash = effect = | 
|  | graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), | 
|  | new_length_and_hash, effect, control); | 
|  |  | 
|  | // Allocate and initialize the new properties. | 
|  | AllocationBuilder a(jsgraph(), effect, control); | 
|  | a.Allocate(PropertyArray::SizeFor(new_length), NOT_TENURED, | 
|  | Type::OtherInternal()); | 
|  | a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant()); | 
|  | a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash); | 
|  | for (int i = 0; i < new_length; ++i) { | 
|  | a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]); | 
|  | } | 
|  | return a.Finish(); | 
|  | } | 
|  |  | 
|  | Node* JSNativeContextSpecialization::BuildCheckEqualsName(Handle<Name> name, | 
|  | Node* value, | 
|  | Node* effect, | 
|  | Node* control) { | 
|  | DCHECK(name->IsUniqueName()); | 
|  | Operator const* const op = | 
|  | name->IsSymbol() ? simplified()->CheckEqualsSymbol() | 
|  | : simplified()->CheckEqualsInternalizedString(); | 
|  | return graph()->NewNode(op, jsgraph()->HeapConstant(name), value, effect, | 
|  | control); | 
|  | } | 
|  |  | 
|  | bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( | 
|  | MapHandles const& receiver_maps) { | 
|  | // Check if all {receiver_maps} either have one of the initial Array.prototype | 
|  | // or Object.prototype objects as their prototype (in any of the current | 
|  | // native contexts, as the global Array protector works isolate-wide). | 
|  | for (Handle<Map> receiver_map : receiver_maps) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | Object* const receiver_prototype = receiver_map->prototype(); | 
|  | if (!isolate()->IsInAnyContext(receiver_prototype, | 
|  | Context::INITIAL_ARRAY_PROTOTYPE_INDEX) && | 
|  | !isolate()->IsInAnyContext(receiver_prototype, | 
|  | Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check if the array prototype chain is intact. | 
|  | if (!isolate()->IsNoElementsProtectorIntact()) return false; | 
|  |  | 
|  | dependencies()->DependOnProtector( | 
|  | PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool JSNativeContextSpecialization::ExtractReceiverMaps( | 
|  | Node* receiver, Node* effect, FeedbackNexus const& nexus, | 
|  | MapHandles* receiver_maps) { | 
|  | DCHECK_EQ(0, receiver_maps->size()); | 
|  | if (nexus.IsUninitialized()) return true; | 
|  |  | 
|  | // See if we can infer a concrete type for the {receiver}. Solely relying on | 
|  | // the inference is not safe for keyed stores, because we would potentially | 
|  | // miss out on transitions that need to be performed. | 
|  | { | 
|  | FeedbackSlotKind kind = nexus.kind(); | 
|  | bool use_inference = | 
|  | !IsKeyedStoreICKind(kind) && !IsStoreInArrayLiteralICKind(kind); | 
|  | if (use_inference && InferReceiverMaps(receiver, effect, receiver_maps)) { | 
|  | // We can assume that {receiver} still has the inferred {receiver_maps}. | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Try to extract some maps from the {nexus}. | 
|  | if (nexus.ExtractMaps(receiver_maps) != 0) { | 
|  | // Try to filter impossible candidates based on inferred root map. | 
|  | Handle<Map> receiver_map; | 
|  | if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) { | 
|  | DCHECK(!receiver_map->is_abandoned_prototype_map()); | 
|  | Isolate* isolate = this->isolate(); | 
|  | receiver_maps->erase( | 
|  | std::remove_if(receiver_maps->begin(), receiver_maps->end(), | 
|  | [receiver_map, isolate](const Handle<Map>& map) { | 
|  | return map->is_abandoned_prototype_map() || | 
|  | map->FindRootMap(isolate) != *receiver_map; | 
|  | }), | 
|  | receiver_maps->end()); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool JSNativeContextSpecialization::InferReceiverMaps( | 
|  | Node* receiver, Node* effect, MapHandles* receiver_maps) { | 
|  | ZoneHandleSet<Map> maps; | 
|  | NodeProperties::InferReceiverMapsResult result = | 
|  | NodeProperties::InferReceiverMaps(isolate(), receiver, effect, &maps); | 
|  | if (result == NodeProperties::kReliableReceiverMaps) { | 
|  | for (size_t i = 0; i < maps.size(); ++i) { | 
|  | receiver_maps->push_back(maps[i]); | 
|  | } | 
|  | return true; | 
|  | } else if (result == NodeProperties::kUnreliableReceiverMaps) { | 
|  | // For untrusted receiver maps, we can still use the information | 
|  | // if the maps are stable. | 
|  | for (size_t i = 0; i < maps.size(); ++i) { | 
|  | if (!maps[i]->is_stable()) return false; | 
|  | } | 
|  | for (size_t i = 0; i < maps.size(); ++i) { | 
|  | receiver_maps->push_back(maps[i]); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap( | 
|  | Node* receiver) { | 
|  | HeapObjectMatcher m(receiver); | 
|  | if (m.HasValue()) { | 
|  | return handle(m.Value()->map()->FindRootMap(isolate()), isolate()); | 
|  | } else if (m.IsJSCreate()) { | 
|  | HeapObjectMatcher mtarget(m.InputAt(0)); | 
|  | HeapObjectMatcher mnewtarget(m.InputAt(1)); | 
|  | if (mtarget.HasValue() && mnewtarget.HasValue()) { | 
|  | Handle<JSFunction> constructor = | 
|  | Handle<JSFunction>::cast(mtarget.Value()); | 
|  | if (constructor->has_initial_map()) { | 
|  | Handle<Map> initial_map(constructor->initial_map(), isolate()); | 
|  | if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) { | 
|  | DCHECK_EQ(*initial_map, initial_map->FindRootMap(isolate())); | 
|  | return initial_map; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return MaybeHandle<Map>(); | 
|  | } | 
|  |  | 
|  | Graph* JSNativeContextSpecialization::graph() const { | 
|  | return jsgraph()->graph(); | 
|  | } | 
|  |  | 
|  | Isolate* JSNativeContextSpecialization::isolate() const { | 
|  | return jsgraph()->isolate(); | 
|  | } | 
|  |  | 
|  | Factory* JSNativeContextSpecialization::factory() const { | 
|  | return isolate()->factory(); | 
|  | } | 
|  |  | 
|  | CommonOperatorBuilder* JSNativeContextSpecialization::common() const { | 
|  | return jsgraph()->common(); | 
|  | } | 
|  |  | 
|  | JSOperatorBuilder* JSNativeContextSpecialization::javascript() const { | 
|  | return jsgraph()->javascript(); | 
|  | } | 
|  |  | 
|  | SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 
|  | return jsgraph()->simplified(); | 
|  | } | 
|  |  | 
|  | #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP | 
|  |  | 
|  | }  // namespace compiler | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |