1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/compiler/js-native-context-specialization.h"
6 
7 #include "src/accessors.h"
8 #include "src/code-factory.h"
9 #include "src/compilation-dependencies.h"
10 #include "src/compiler/access-builder.h"
11 #include "src/compiler/access-info.h"
12 #include "src/compiler/js-graph.h"
13 #include "src/compiler/js-operator.h"
14 #include "src/compiler/linkage.h"
15 #include "src/compiler/node-matchers.h"
16 #include "src/compiler/type-cache.h"
17 #include "src/field-index-inl.h"
18 #include "src/isolate-inl.h"
19 #include "src/type-feedback-vector.h"
20 
21 namespace v8 {
22 namespace internal {
23 namespace compiler {
24 
25 namespace {
26 
HasNumberMaps(MapList const & maps)27 bool HasNumberMaps(MapList const& maps) {
28   for (auto map : maps) {
29     if (map->instance_type() == HEAP_NUMBER_TYPE) return true;
30   }
31   return false;
32 }
33 
HasOnlyJSArrayMaps(MapList const & maps)34 bool HasOnlyJSArrayMaps(MapList const& maps) {
35   for (auto map : maps) {
36     if (!map->IsJSArrayMap()) return false;
37   }
38   return true;
39 }
40 
HasOnlyNumberMaps(MapList const & maps)41 bool HasOnlyNumberMaps(MapList const& maps) {
42   for (auto map : maps) {
43     if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
44   }
45   return true;
46 }
47 
48 template <typename T>
HasOnlyStringMaps(T const & maps)49 bool HasOnlyStringMaps(T const& maps) {
50   for (auto map : maps) {
51     if (!map->IsStringMap()) return false;
52   }
53   return true;
54 }
55 
56 }  // namespace
57 
JSNativeContextSpecialization(Editor * editor,JSGraph * jsgraph,Flags flags,Handle<Context> native_context,CompilationDependencies * dependencies,Zone * zone)58 JSNativeContextSpecialization::JSNativeContextSpecialization(
59     Editor* editor, JSGraph* jsgraph, Flags flags,
60     Handle<Context> native_context, CompilationDependencies* dependencies,
61     Zone* zone)
62     : AdvancedReducer(editor),
63       jsgraph_(jsgraph),
64       flags_(flags),
65       native_context_(native_context),
66       dependencies_(dependencies),
67       zone_(zone),
68       type_cache_(TypeCache::Get()) {}
69 
Reduce(Node * node)70 Reduction JSNativeContextSpecialization::Reduce(Node* node) {
71   switch (node->opcode()) {
72     case IrOpcode::kJSInstanceOf:
73       return ReduceJSInstanceOf(node);
74     case IrOpcode::kJSLoadContext:
75       return ReduceJSLoadContext(node);
76     case IrOpcode::kJSLoadNamed:
77       return ReduceJSLoadNamed(node);
78     case IrOpcode::kJSStoreNamed:
79       return ReduceJSStoreNamed(node);
80     case IrOpcode::kJSLoadProperty:
81       return ReduceJSLoadProperty(node);
82     case IrOpcode::kJSStoreProperty:
83       return ReduceJSStoreProperty(node);
84     default:
85       break;
86   }
87   return NoChange();
88 }
89 
ReduceJSInstanceOf(Node * node)90 Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
91   DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
92   Node* object = NodeProperties::GetValueInput(node, 0);
93   Node* constructor = NodeProperties::GetValueInput(node, 1);
94   Node* context = NodeProperties::GetContextInput(node);
95   Node* effect = NodeProperties::GetEffectInput(node);
96   Node* control = NodeProperties::GetControlInput(node);
97 
98   // If deoptimization is disabled, we cannot optimize.
99   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
100 
101   // Check if the right hand side is a known {receiver}.
102   HeapObjectMatcher m(constructor);
103   if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange();
104   Handle<JSObject> receiver = Handle<JSObject>::cast(m.Value());
105   Handle<Map> receiver_map(receiver->map(), isolate());
106 
107   // Compute property access info for @@hasInstance on {receiver}.
108   PropertyAccessInfo access_info;
109   AccessInfoFactory access_info_factory(dependencies(), native_context(),
110                                         graph()->zone());
111   if (!access_info_factory.ComputePropertyAccessInfo(
112           receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad,
113           &access_info)) {
114     return NoChange();
115   }
116 
117   if (access_info.IsNotFound()) {
118     // If there's no @@hasInstance handler, the OrdinaryHasInstance operation
119     // takes over, but that requires the {receiver} to be callable.
120     if (receiver->IsCallable()) {
121       // Determine actual holder and perform prototype chain checks.
122       Handle<JSObject> holder;
123       if (access_info.holder().ToHandle(&holder)) {
124         AssumePrototypesStable(access_info.receiver_maps(), holder);
125       }
126 
127       // Monomorphic property access.
128       effect =
129           BuildCheckMaps(constructor, effect, control, MapList{receiver_map});
130 
131       // Lower to OrdinaryHasInstance(C, O).
132       NodeProperties::ReplaceValueInput(node, constructor, 0);
133       NodeProperties::ReplaceValueInput(node, object, 1);
134       NodeProperties::ReplaceEffectInput(node, effect);
135       NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
136       return Changed(node);
137     }
138   } else if (access_info.IsDataConstant()) {
139     DCHECK(access_info.constant()->IsCallable());
140 
141     // Determine actual holder and perform prototype chain checks.
142     Handle<JSObject> holder;
143     if (access_info.holder().ToHandle(&holder)) {
144       AssumePrototypesStable(access_info.receiver_maps(), holder);
145     }
146 
147     // Monomorphic property access.
148     effect =
149         BuildCheckMaps(constructor, effect, control, MapList{receiver_map});
150 
151     // Call the @@hasInstance handler.
152     Node* target = jsgraph()->Constant(access_info.constant());
153     node->InsertInput(graph()->zone(), 0, target);
154     node->ReplaceInput(1, constructor);
155     node->ReplaceInput(2, object);
156     node->ReplaceInput(5, effect);
157     NodeProperties::ChangeOp(
158         node,
159         javascript()->CallFunction(3, 0.0f, VectorSlotPair(),
160                                    ConvertReceiverMode::kNotNullOrUndefined));
161 
162     // Rewire the value uses of {node} to ToBoolean conversion of the result.
163     Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
164                                    node, context);
165     for (Edge edge : node->use_edges()) {
166       if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
167         edge.UpdateTo(value);
168         Revisit(edge.from());
169       }
170     }
171     return Changed(node);
172   }
173 
174   return NoChange();
175 }
176 
ReduceJSLoadContext(Node * node)177 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
178   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
179   ContextAccess const& access = ContextAccessOf(node->op());
180   // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native
181   // context (if any), so we can constant-fold those fields, which is
182   // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable.
183   if (access.index() == Context::NATIVE_CONTEXT_INDEX) {
184     Node* value = jsgraph()->HeapConstant(native_context());
185     ReplaceWithValue(node, value);
186     return Replace(value);
187   }
188   return NoChange();
189 }
190 
ReduceNamedAccess(Node * node,Node * value,MapHandleList const & receiver_maps,Handle<Name> name,AccessMode access_mode,LanguageMode language_mode,Handle<TypeFeedbackVector> vector,FeedbackVectorSlot slot,Node * index)191 Reduction JSNativeContextSpecialization::ReduceNamedAccess(
192     Node* node, Node* value, MapHandleList const& receiver_maps,
193     Handle<Name> name, AccessMode access_mode, LanguageMode language_mode,
194     Handle<TypeFeedbackVector> vector, FeedbackVectorSlot slot, Node* index) {
195   DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
196          node->opcode() == IrOpcode::kJSStoreNamed ||
197          node->opcode() == IrOpcode::kJSLoadProperty ||
198          node->opcode() == IrOpcode::kJSStoreProperty);
199   Node* receiver = NodeProperties::GetValueInput(node, 0);
200   Node* context = NodeProperties::GetContextInput(node);
201   Node* frame_state_eager = NodeProperties::FindFrameStateBefore(node);
202   Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node);
203   Node* effect = NodeProperties::GetEffectInput(node);
204   Node* control = NodeProperties::GetControlInput(node);
205 
206   // Not much we can do if deoptimization support is disabled.
207   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
208 
209   // Compute property access infos for the receiver maps.
210   AccessInfoFactory access_info_factory(dependencies(), native_context(),
211                                         graph()->zone());
212   ZoneVector<PropertyAccessInfo> access_infos(zone());
213   if (!access_info_factory.ComputePropertyAccessInfos(
214           receiver_maps, name, access_mode, &access_infos)) {
215     return NoChange();
216   }
217 
218   // TODO(turbofan): Add support for inlining into try blocks.
219   bool is_exceptional = NodeProperties::IsExceptionalCall(node);
220   for (auto access_info : access_infos) {
221     if (access_info.IsAccessorConstant()) {
222       // Accessor in try-blocks are not supported yet.
223       if (is_exceptional || !(flags() & kAccessorInliningEnabled)) {
224         return NoChange();
225       }
226     } else if (access_info.IsGeneric()) {
227       // We do not handle generic calls in try blocks.
228       if (is_exceptional) return NoChange();
229       // We only handle the generic store IC case.
230       if (vector->GetKind(slot) != FeedbackVectorSlotKind::STORE_IC) {
231         return NoChange();
232       }
233     }
234   }
235 
236   // Nothing to do if we have no non-deprecated maps.
237   if (access_infos.empty()) {
238     return ReduceSoftDeoptimize(
239         node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
240   }
241 
242   // Ensure that {index} matches the specified {name} (if {index} is given).
243   if (index != nullptr) {
244     Node* check = graph()->NewNode(simplified()->ReferenceEqual(), index,
245                                    jsgraph()->HeapConstant(name));
246     effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
247   }
248 
249   // Check for the monomorphic cases.
250   if (access_infos.size() == 1) {
251     PropertyAccessInfo access_info = access_infos.front();
252     if (HasOnlyStringMaps(access_info.receiver_maps())) {
253       // Monormorphic string access (ignoring the fact that there are multiple
254       // String maps).
255       receiver = effect = graph()->NewNode(simplified()->CheckString(),
256                                            receiver, effect, control);
257     } else if (HasOnlyNumberMaps(access_info.receiver_maps())) {
258       // Monomorphic number access (we also deal with Smis here).
259       receiver = effect = graph()->NewNode(simplified()->CheckNumber(),
260                                            receiver, effect, control);
261     } else {
262       // Monomorphic property access.
263       receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
264                                            receiver, effect, control);
265       effect = BuildCheckMaps(receiver, effect, control,
266                               access_info.receiver_maps());
267     }
268 
269     // Generate the actual property access.
270     ValueEffectControl continuation = BuildPropertyAccess(
271         receiver, value, context, frame_state_lazy, effect, control, name,
272         access_info, access_mode, language_mode, vector, slot);
273     value = continuation.value();
274     effect = continuation.effect();
275     control = continuation.control();
276   } else {
277     // The final states for every polymorphic branch. We join them with
278     // Merge+Phi+EffectPhi at the bottom.
279     ZoneVector<Node*> values(zone());
280     ZoneVector<Node*> effects(zone());
281     ZoneVector<Node*> controls(zone());
282 
283     // Check if {receiver} may be a number.
284     bool receiverissmi_possible = false;
285     for (PropertyAccessInfo const& access_info : access_infos) {
286       if (HasNumberMaps(access_info.receiver_maps())) {
287         receiverissmi_possible = true;
288         break;
289       }
290     }
291 
292     // Ensure that {receiver} is a heap object.
293     Node* receiverissmi_control = nullptr;
294     Node* receiverissmi_effect = effect;
295     if (receiverissmi_possible) {
296       Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
297       Node* branch = graph()->NewNode(common()->Branch(), check, control);
298       control = graph()->NewNode(common()->IfFalse(), branch);
299       receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
300       receiverissmi_effect = effect;
301     } else {
302       receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
303                                            receiver, effect, control);
304     }
305 
306     // Load the {receiver} map. The resulting effect is the dominating effect
307     // for all (polymorphic) branches.
308     Node* receiver_map = effect =
309         graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
310                          receiver, effect, control);
311 
312     // Generate code for the various different property access patterns.
313     Node* fallthrough_control = control;
314     for (size_t j = 0; j < access_infos.size(); ++j) {
315       PropertyAccessInfo const& access_info = access_infos[j];
316       Node* this_value = value;
317       Node* this_receiver = receiver;
318       Node* this_effect = effect;
319       Node* this_control = fallthrough_control;
320 
321       // Perform map check on {receiver}.
322       MapList const& receiver_maps = access_info.receiver_maps();
323       {
324         // Emit a (sequence of) map checks for other {receiver}s.
325         ZoneVector<Node*> this_controls(zone());
326         ZoneVector<Node*> this_effects(zone());
327         if (j == access_infos.size() - 1) {
328           // Last map check on the fallthrough control path, do a
329           // conditional eager deoptimization exit here.
330           this_effect = BuildCheckMaps(receiver, this_effect, this_control,
331                                        receiver_maps);
332           this_effects.push_back(this_effect);
333           this_controls.push_back(fallthrough_control);
334           fallthrough_control = nullptr;
335         } else {
336           for (auto map : receiver_maps) {
337             Node* check =
338                 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
339                                  jsgraph()->Constant(map));
340             Node* branch = graph()->NewNode(common()->Branch(), check,
341                                             fallthrough_control);
342             fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
343             this_controls.push_back(
344                 graph()->NewNode(common()->IfTrue(), branch));
345             this_effects.push_back(this_effect);
346           }
347         }
348 
349         // The Number case requires special treatment to also deal with Smis.
350         if (HasNumberMaps(receiver_maps)) {
351           // Join this check with the "receiver is smi" check above.
352           DCHECK_NOT_NULL(receiverissmi_effect);
353           DCHECK_NOT_NULL(receiverissmi_control);
354           this_effects.push_back(receiverissmi_effect);
355           this_controls.push_back(receiverissmi_control);
356           receiverissmi_effect = receiverissmi_control = nullptr;
357         }
358 
359         // Create single chokepoint for the control.
360         int const this_control_count = static_cast<int>(this_controls.size());
361         if (this_control_count == 1) {
362           this_control = this_controls.front();
363           this_effect = this_effects.front();
364         } else {
365           this_control =
366               graph()->NewNode(common()->Merge(this_control_count),
367                                this_control_count, &this_controls.front());
368           this_effects.push_back(this_control);
369           this_effect =
370               graph()->NewNode(common()->EffectPhi(this_control_count),
371                                this_control_count + 1, &this_effects.front());
372 
373           // TODO(turbofan): The effect/control linearization will not find a
374           // FrameState after the EffectPhi that is generated above.
375           this_effect =
376               graph()->NewNode(common()->Checkpoint(), frame_state_eager,
377                                this_effect, this_control);
378         }
379       }
380 
381       // Generate the actual property access.
382       ValueEffectControl continuation = BuildPropertyAccess(
383           this_receiver, this_value, context, frame_state_lazy, this_effect,
384           this_control, name, access_info, access_mode, language_mode, vector,
385           slot);
386       values.push_back(continuation.value());
387       effects.push_back(continuation.effect());
388       controls.push_back(continuation.control());
389     }
390 
391     DCHECK_NULL(fallthrough_control);
392 
393     // Generate the final merge point for all (polymorphic) branches.
394     int const control_count = static_cast<int>(controls.size());
395     if (control_count == 0) {
396       value = effect = control = jsgraph()->Dead();
397     } else if (control_count == 1) {
398       value = values.front();
399       effect = effects.front();
400       control = controls.front();
401     } else {
402       control = graph()->NewNode(common()->Merge(control_count), control_count,
403                                  &controls.front());
404       values.push_back(control);
405       value = graph()->NewNode(
406           common()->Phi(MachineRepresentation::kTagged, control_count),
407           control_count + 1, &values.front());
408       effects.push_back(control);
409       effect = graph()->NewNode(common()->EffectPhi(control_count),
410                                 control_count + 1, &effects.front());
411     }
412   }
413   ReplaceWithValue(node, value, effect, control);
414   return Replace(value);
415 }
416 
ReduceNamedAccessFromNexus(Node * node,Node * value,FeedbackNexus const & nexus,Handle<Name> name,AccessMode access_mode,LanguageMode language_mode)417 Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
418     Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name,
419     AccessMode access_mode, LanguageMode language_mode) {
420   DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
421          node->opcode() == IrOpcode::kJSStoreNamed);
422   Node* const receiver = NodeProperties::GetValueInput(node, 0);
423   Node* const effect = NodeProperties::GetEffectInput(node);
424 
425   // Check if the {nexus} reports type feedback for the IC.
426   if (nexus.IsUninitialized()) {
427     if ((flags() & kDeoptimizationEnabled) &&
428         (flags() & kBailoutOnUninitialized)) {
429       return ReduceSoftDeoptimize(
430           node,
431           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
432     }
433     return NoChange();
434   }
435 
436   // Extract receiver maps from the IC using the {nexus}.
437   MapHandleList receiver_maps;
438   if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
439     return NoChange();
440   } else if (receiver_maps.length() == 0) {
441     if ((flags() & kDeoptimizationEnabled) &&
442         (flags() & kBailoutOnUninitialized)) {
443       return ReduceSoftDeoptimize(
444           node,
445           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
446     }
447     return NoChange();
448   }
449 
450   // Try to lower the named access based on the {receiver_maps}.
451   return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
452                            language_mode, nexus.vector_handle(), nexus.slot());
453 }
454 
455 
ReduceJSLoadNamed(Node * node)456 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
457   DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
458   NamedAccess const& p = NamedAccessOf(node->op());
459   Node* const receiver = NodeProperties::GetValueInput(node, 0);
460   Node* const value = jsgraph()->Dead();
461 
462   // Check if we have a constant receiver.
463   HeapObjectMatcher m(receiver);
464   if (m.HasValue()) {
465     if (m.Value()->IsJSFunction() &&
466         p.name().is_identical_to(factory()->prototype_string())) {
467       // Optimize "prototype" property of functions.
468       Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
469       if (function->has_initial_map()) {
470         // We need to add a code dependency on the initial map of the
471         // {function} in order to be notified about changes to the
472         // "prototype" of {function}, so it doesn't make sense to
473         // continue unless deoptimization is enabled.
474         if (flags() & kDeoptimizationEnabled) {
475           Handle<Map> initial_map(function->initial_map(), isolate());
476           dependencies()->AssumeInitialMapCantChange(initial_map);
477           Handle<Object> prototype(initial_map->prototype(), isolate());
478           Node* value = jsgraph()->Constant(prototype);
479           ReplaceWithValue(node, value);
480           return Replace(value);
481         }
482       }
483     } else if (m.Value()->IsString() &&
484                p.name().is_identical_to(factory()->length_string())) {
485       // Constant-fold "length" property on constant strings.
486       Handle<String> string = Handle<String>::cast(m.Value());
487       Node* value = jsgraph()->Constant(string->length());
488       ReplaceWithValue(node, value);
489       return Replace(value);
490     }
491   }
492 
493   // Extract receiver maps from the LOAD_IC using the LoadICNexus.
494   if (!p.feedback().IsValid()) return NoChange();
495   LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
496 
497   // Try to lower the named access based on the {receiver_maps}.
498   return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
499                                     AccessMode::kLoad, p.language_mode());
500 }
501 
502 
ReduceJSStoreNamed(Node * node)503 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
504   DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
505   NamedAccess const& p = NamedAccessOf(node->op());
506   Node* const value = NodeProperties::GetValueInput(node, 1);
507 
508   // Extract receiver maps from the STORE_IC using the StoreICNexus.
509   if (!p.feedback().IsValid()) return NoChange();
510   StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
511 
512   // Try to lower the named access based on the {receiver_maps}.
513   return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
514                                     AccessMode::kStore, p.language_mode());
515 }
516 
517 
ReduceElementAccess(Node * node,Node * index,Node * value,MapHandleList const & receiver_maps,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)518 Reduction JSNativeContextSpecialization::ReduceElementAccess(
519     Node* node, Node* index, Node* value, MapHandleList const& receiver_maps,
520     AccessMode access_mode, LanguageMode language_mode,
521     KeyedAccessStoreMode store_mode) {
522   DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
523          node->opcode() == IrOpcode::kJSStoreProperty);
524   Node* receiver = NodeProperties::GetValueInput(node, 0);
525   Node* effect = NodeProperties::GetEffectInput(node);
526   Node* control = NodeProperties::GetControlInput(node);
527   Node* frame_state = NodeProperties::FindFrameStateBefore(node);
528 
529   // Not much we can do if deoptimization support is disabled.
530   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
531 
532   // Check for keyed access to strings.
533   if (HasOnlyStringMaps(receiver_maps)) {
534     // Strings are immutable in JavaScript.
535     if (access_mode == AccessMode::kStore) return NoChange();
536 
537     // Ensure that the {receiver} is actually a String.
538     receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver,
539                                          effect, control);
540 
541     // Determine the {receiver} length.
542     Node* length = effect = graph()->NewNode(
543         simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
544         effect, control);
545 
546     // Ensure that {index} is less than {receiver} length.
547     index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
548                                       length, effect, control);
549 
550     // Load the character from the {receiver}.
551     value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, index,
552                              control);
553 
554     // Return it as a single character string.
555     value = graph()->NewNode(simplified()->StringFromCharCode(), value);
556   } else {
557     // Retrieve the native context from the given {node}.
558     // Compute element access infos for the receiver maps.
559     AccessInfoFactory access_info_factory(dependencies(), native_context(),
560                                           graph()->zone());
561     ZoneVector<ElementAccessInfo> access_infos(zone());
562     if (!access_info_factory.ComputeElementAccessInfos(
563             receiver_maps, access_mode, &access_infos)) {
564       return NoChange();
565     }
566 
567     // Nothing to do if we have no non-deprecated maps.
568     if (access_infos.empty()) {
569       return ReduceSoftDeoptimize(
570           node,
571           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
572     }
573 
574     // For holey stores or growing stores, we need to check that the prototype
575     // chain contains no setters for elements, and we need to guard those checks
576     // via code dependencies on the relevant prototype maps.
577     if (access_mode == AccessMode::kStore) {
578       // TODO(turbofan): We could have a fast path here, that checks for the
579       // common case of Array or Object prototype only and therefore avoids
580       // the zone allocation of this vector.
581       ZoneVector<Handle<Map>> prototype_maps(zone());
582       for (ElementAccessInfo const& access_info : access_infos) {
583         for (Handle<Map> receiver_map : access_info.receiver_maps()) {
584           // If the {receiver_map} has a prototype and it's elements backing
585           // store is either holey, or we have a potentially growing store,
586           // then we need to check that all prototypes have stable maps with
587           // fast elements (and we need to guard against changes to that below).
588           if (IsHoleyElementsKind(receiver_map->elements_kind()) ||
589               IsGrowStoreMode(store_mode)) {
590             // Make sure all prototypes are stable and have fast elements.
591             for (Handle<Map> map = receiver_map;;) {
592               Handle<Object> map_prototype(map->prototype(), isolate());
593               if (map_prototype->IsNull(isolate())) break;
594               if (!map_prototype->IsJSObject()) return NoChange();
595               map = handle(Handle<JSObject>::cast(map_prototype)->map(),
596                            isolate());
597               if (!map->is_stable()) return NoChange();
598               if (!IsFastElementsKind(map->elements_kind())) return NoChange();
599               prototype_maps.push_back(map);
600             }
601           }
602         }
603       }
604 
605       // Install dependencies on the relevant prototype maps.
606       for (Handle<Map> prototype_map : prototype_maps) {
607         dependencies()->AssumeMapStable(prototype_map);
608       }
609     }
610 
611     // Ensure that {receiver} is a heap object.
612     receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
613                                          receiver, effect, control);
614 
615     // Check for the monomorphic case.
616     if (access_infos.size() == 1) {
617       ElementAccessInfo access_info = access_infos.front();
618 
619       // Perform possible elements kind transitions.
620       for (auto transition : access_info.transitions()) {
621         Handle<Map> const transition_source = transition.first;
622         Handle<Map> const transition_target = transition.second;
623         effect = graph()->NewNode(
624             simplified()->TransitionElementsKind(
625                 IsSimpleMapChangeTransition(transition_source->elements_kind(),
626                                             transition_target->elements_kind())
627                     ? ElementsTransition::kFastTransition
628                     : ElementsTransition::kSlowTransition),
629             receiver, jsgraph()->HeapConstant(transition_source),
630             jsgraph()->HeapConstant(transition_target), effect, control);
631       }
632 
633       // TODO(turbofan): The effect/control linearization will not find a
634       // FrameState after the StoreField or Call that is generated for the
635       // elements kind transition above. This is because those operators
636       // don't have the kNoWrite flag on it, even though they are not
637       // observable by JavaScript.
638       effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect,
639                                 control);
640 
641       // Perform map check on the {receiver}.
642       effect = BuildCheckMaps(receiver, effect, control,
643                               access_info.receiver_maps());
644 
645       // Access the actual element.
646       ValueEffectControl continuation =
647           BuildElementAccess(receiver, index, value, effect, control,
648                              access_info, access_mode, store_mode);
649       value = continuation.value();
650       effect = continuation.effect();
651       control = continuation.control();
652     } else {
653       // The final states for every polymorphic branch. We join them with
654       // Merge+Phi+EffectPhi at the bottom.
655       ZoneVector<Node*> values(zone());
656       ZoneVector<Node*> effects(zone());
657       ZoneVector<Node*> controls(zone());
658 
659       // Generate code for the various different element access patterns.
660       Node* fallthrough_control = control;
661       for (size_t j = 0; j < access_infos.size(); ++j) {
662         ElementAccessInfo const& access_info = access_infos[j];
663         Node* this_receiver = receiver;
664         Node* this_value = value;
665         Node* this_index = index;
666         Node* this_effect = effect;
667         Node* this_control = fallthrough_control;
668 
669         // Perform possible elements kind transitions.
670         for (auto transition : access_info.transitions()) {
671           Handle<Map> const transition_source = transition.first;
672           Handle<Map> const transition_target = transition.second;
673           this_effect = graph()->NewNode(
674               simplified()->TransitionElementsKind(
675                   IsSimpleMapChangeTransition(
676                       transition_source->elements_kind(),
677                       transition_target->elements_kind())
678                       ? ElementsTransition::kFastTransition
679                       : ElementsTransition::kSlowTransition),
680               receiver, jsgraph()->HeapConstant(transition_source),
681               jsgraph()->HeapConstant(transition_target), this_effect,
682               this_control);
683         }
684 
685         // Load the {receiver} map.
686         Node* receiver_map = this_effect =
687             graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
688                              receiver, this_effect, this_control);
689 
690         // Perform map check(s) on {receiver}.
691         MapList const& receiver_maps = access_info.receiver_maps();
692         if (j == access_infos.size() - 1) {
693           // Last map check on the fallthrough control path, do a
694           // conditional eager deoptimization exit here.
695           this_effect = BuildCheckMaps(receiver, this_effect, this_control,
696                                        receiver_maps);
697           fallthrough_control = nullptr;
698         } else {
699           ZoneVector<Node*> this_controls(zone());
700           ZoneVector<Node*> this_effects(zone());
701           for (Handle<Map> map : receiver_maps) {
702             Node* check =
703                 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
704                                  jsgraph()->Constant(map));
705             Node* branch = graph()->NewNode(common()->Branch(), check,
706                                             fallthrough_control);
707             this_controls.push_back(
708                 graph()->NewNode(common()->IfTrue(), branch));
709             this_effects.push_back(this_effect);
710             fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
711           }
712 
713           // Create single chokepoint for the control.
714           int const this_control_count = static_cast<int>(this_controls.size());
715           if (this_control_count == 1) {
716             this_control = this_controls.front();
717             this_effect = this_effects.front();
718           } else {
719             this_control =
720                 graph()->NewNode(common()->Merge(this_control_count),
721                                  this_control_count, &this_controls.front());
722             this_effects.push_back(this_control);
723             this_effect =
724                 graph()->NewNode(common()->EffectPhi(this_control_count),
725                                  this_control_count + 1, &this_effects.front());
726 
727             // TODO(turbofan): The effect/control linearization will not find a
728             // FrameState after the EffectPhi that is generated above.
729             this_effect = graph()->NewNode(common()->Checkpoint(), frame_state,
730                                            this_effect, this_control);
731           }
732         }
733 
734         // Access the actual element.
735         ValueEffectControl continuation = BuildElementAccess(
736             this_receiver, this_index, this_value, this_effect, this_control,
737             access_info, access_mode, store_mode);
738         values.push_back(continuation.value());
739         effects.push_back(continuation.effect());
740         controls.push_back(continuation.control());
741       }
742 
743       DCHECK_NULL(fallthrough_control);
744 
745       // Generate the final merge point for all (polymorphic) branches.
746       int const control_count = static_cast<int>(controls.size());
747       if (control_count == 0) {
748         value = effect = control = jsgraph()->Dead();
749       } else if (control_count == 1) {
750         value = values.front();
751         effect = effects.front();
752         control = controls.front();
753       } else {
754         control = graph()->NewNode(common()->Merge(control_count),
755                                    control_count, &controls.front());
756         values.push_back(control);
757         value = graph()->NewNode(
758             common()->Phi(MachineRepresentation::kTagged, control_count),
759             control_count + 1, &values.front());
760         effects.push_back(control);
761         effect = graph()->NewNode(common()->EffectPhi(control_count),
762                                   control_count + 1, &effects.front());
763       }
764     }
765   }
766 
767   ReplaceWithValue(node, value, effect, control);
768   return Replace(value);
769 }
770 
771 template <typename KeyedICNexus>
ReduceKeyedAccess(Node * node,Node * index,Node * value,KeyedICNexus const & nexus,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)772 Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
773     Node* node, Node* index, Node* value, KeyedICNexus const& nexus,
774     AccessMode access_mode, LanguageMode language_mode,
775     KeyedAccessStoreMode store_mode) {
776   DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
777          node->opcode() == IrOpcode::kJSStoreProperty);
778   Node* receiver = NodeProperties::GetValueInput(node, 0);
779   Node* effect = NodeProperties::GetEffectInput(node);
780   Node* control = NodeProperties::GetControlInput(node);
781 
782   // Optimize access for constant {receiver}.
783   HeapObjectMatcher mreceiver(receiver);
784   if (mreceiver.HasValue() && mreceiver.Value()->IsString()) {
785     Handle<String> string = Handle<String>::cast(mreceiver.Value());
786 
787     // We can only assume that the {index} is a valid array index if the IC
788     // is in element access mode and not MEGAMORPHIC, otherwise there's no
789     // guard for the bounds check below.
790     if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) {
791       // Strings are immutable in JavaScript.
792       if (access_mode == AccessMode::kStore) return NoChange();
793 
794       // Properly deal with constant {index}.
795       NumberMatcher mindex(index);
796       if (mindex.IsInteger() && mindex.IsInRange(0.0, string->length() - 1)) {
797         // Constant-fold the {index} access to {string}.
798         Node* value = jsgraph()->HeapConstant(
799             factory()->LookupSingleCharacterStringFromCode(
800                 string->Get(static_cast<int>(mindex.Value()))));
801         ReplaceWithValue(node, value, effect, control);
802         return Replace(value);
803       } else if (flags() & kDeoptimizationEnabled) {
804         // Ensure that {index} is less than {receiver} length.
805         Node* length = jsgraph()->Constant(string->length());
806         index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
807                                           length, effect, control);
808 
809         // Load the character from the {receiver}.
810         value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
811                                  index, control);
812 
813         // Return it as a single character string.
814         value = graph()->NewNode(simplified()->StringFromCharCode(), value);
815         ReplaceWithValue(node, value, effect, control);
816         return Replace(value);
817       }
818     }
819   }
820 
821   // Check if the {nexus} reports type feedback for the IC.
822   if (nexus.IsUninitialized()) {
823     if ((flags() & kDeoptimizationEnabled) &&
824         (flags() & kBailoutOnUninitialized)) {
825       return ReduceSoftDeoptimize(
826           node,
827           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
828     }
829     return NoChange();
830   }
831 
832   // Extract receiver maps from the {nexus}.
833   MapHandleList receiver_maps;
834   if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
835     return NoChange();
836   } else if (receiver_maps.length() == 0) {
837     if ((flags() & kDeoptimizationEnabled) &&
838         (flags() & kBailoutOnUninitialized)) {
839       return ReduceSoftDeoptimize(
840           node,
841           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
842     }
843     return NoChange();
844   }
845 
846   // Optimize access for constant {index}.
847   HeapObjectMatcher mindex(index);
848   if (mindex.HasValue() && mindex.Value()->IsPrimitive()) {
849     // Keyed access requires a ToPropertyKey on the {index} first before
850     // looking up the property on the object (see ES6 section 12.3.2.1).
851     // We can only do this for non-observable ToPropertyKey invocations,
852     // so we limit the constant indices to primitives at this point.
853     Handle<Name> name;
854     if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) {
855       uint32_t array_index;
856       if (name->AsArrayIndex(&array_index)) {
857         // Use the constant array index.
858         index = jsgraph()->Constant(static_cast<double>(array_index));
859       } else {
860         name = factory()->InternalizeName(name);
861         return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
862                                  language_mode, nexus.vector_handle(),
863                                  nexus.slot());
864       }
865     }
866   }
867 
868   // Check if we have feedback for a named access.
869   if (Name* name = nexus.FindFirstName()) {
870     return ReduceNamedAccess(
871         node, value, receiver_maps, handle(name, isolate()), access_mode,
872         language_mode, nexus.vector_handle(), nexus.slot(), index);
873   } else if (nexus.GetKeyType() != ELEMENT) {
874     // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume
875     // that the {index} is a valid array index, thus we just let the IC continue
876     // to deal with this load/store.
877     return NoChange();
878   } else if (nexus.ic_state() == MEGAMORPHIC) {
879     // The KeyedLoad/StoreIC uses the MEGAMORPHIC state to guard the assumption
880     // that a numeric {index} is within the valid bounds for {receiver}, i.e.
881     // it transitions to MEGAMORPHIC once it sees an out-of-bounds access. Thus
882     // we cannot continue here if the IC state is MEGAMORPHIC.
883     return NoChange();
884   }
885 
886   // Try to lower the element access based on the {receiver_maps}.
887   return ReduceElementAccess(node, index, value, receiver_maps, access_mode,
888                              language_mode, store_mode);
889 }
890 
ReduceSoftDeoptimize(Node * node,DeoptimizeReason reason)891 Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
892     Node* node, DeoptimizeReason reason) {
893   Node* effect = NodeProperties::GetEffectInput(node);
894   Node* control = NodeProperties::GetControlInput(node);
895   Node* frame_state = NodeProperties::FindFrameStateBefore(node);
896   Node* deoptimize =
897       graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason),
898                        frame_state, effect, control);
899   // TODO(bmeurer): This should be on the AdvancedReducer somehow.
900   NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
901   Revisit(graph()->end());
902   node->TrimInputCount(0);
903   NodeProperties::ChangeOp(node, common()->Dead());
904   return Changed(node);
905 }
906 
907 
ReduceJSLoadProperty(Node * node)908 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
909   DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
910   PropertyAccess const& p = PropertyAccessOf(node->op());
911   Node* const index = NodeProperties::GetValueInput(node, 1);
912   Node* const value = jsgraph()->Dead();
913 
914   // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus.
915   if (!p.feedback().IsValid()) return NoChange();
916   KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
917 
918   // Try to lower the keyed access based on the {nexus}.
919   return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad,
920                            p.language_mode(), STANDARD_STORE);
921 }
922 
923 
ReduceJSStoreProperty(Node * node)924 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
925   DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode());
926   PropertyAccess const& p = PropertyAccessOf(node->op());
927   Node* const index = NodeProperties::GetValueInput(node, 1);
928   Node* const value = NodeProperties::GetValueInput(node, 2);
929 
930   // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus.
931   if (!p.feedback().IsValid()) return NoChange();
932   KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
933 
934   // Extract the keyed access store mode from the KEYED_STORE_IC.
935   KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode();
936 
937   // Try to lower the keyed access based on the {nexus}.
938   return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore,
939                            p.language_mode(), store_mode);
940 }
941 
942 JSNativeContextSpecialization::ValueEffectControl
BuildPropertyAccess(Node * receiver,Node * value,Node * context,Node * frame_state,Node * effect,Node * control,Handle<Name> name,PropertyAccessInfo const & access_info,AccessMode access_mode,LanguageMode language_mode,Handle<TypeFeedbackVector> vector,FeedbackVectorSlot slot)943 JSNativeContextSpecialization::BuildPropertyAccess(
944     Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
945     Node* control, Handle<Name> name, PropertyAccessInfo const& access_info,
946     AccessMode access_mode, LanguageMode language_mode,
947     Handle<TypeFeedbackVector> vector, FeedbackVectorSlot slot) {
948   // Determine actual holder and perform prototype chain checks.
949   Handle<JSObject> holder;
950   if (access_info.holder().ToHandle(&holder)) {
951     AssumePrototypesStable(access_info.receiver_maps(), holder);
952   }
953 
954   // Generate the actual property access.
955   if (access_info.IsNotFound()) {
956     DCHECK_EQ(AccessMode::kLoad, access_mode);
957     value = jsgraph()->UndefinedConstant();
958   } else if (access_info.IsDataConstant()) {
959     Node* constant_value = jsgraph()->Constant(access_info.constant());
960     if (access_mode == AccessMode::kStore) {
961       Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value,
962                                      constant_value);
963       effect =
964           graph()->NewNode(simplified()->CheckIf(), check, effect, control);
965     }
966     value = constant_value;
967   } else if (access_info.IsAccessorConstant()) {
968     // TODO(bmeurer): Properly rewire the IfException edge here if there's any.
969     Node* target = jsgraph()->Constant(access_info.constant());
970     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
971     Handle<SharedFunctionInfo> shared_info =
972         frame_info.shared_info().ToHandleChecked();
973     switch (access_mode) {
974       case AccessMode::kLoad: {
975         // We need a FrameState for the getter stub to restore the correct
976         // context before returning to fullcodegen.
977         FrameStateFunctionInfo const* frame_info0 =
978             common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub,
979                                                    1, 0, shared_info);
980         Node* frame_state0 = graph()->NewNode(
981             common()->FrameState(BailoutId::None(),
982                                  OutputFrameStateCombine::Ignore(),
983                                  frame_info0),
984             graph()->NewNode(common()->StateValues(1), receiver),
985             jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
986             context, target, frame_state);
987 
988         // Introduce the call to the getter function.
989         if (access_info.constant()->IsJSFunction()) {
990           value = effect = graph()->NewNode(
991               javascript()->CallFunction(
992                   2, 0.0f, VectorSlotPair(),
993                   ConvertReceiverMode::kNotNullOrUndefined),
994               target, receiver, context, frame_state0, effect, control);
995           control = graph()->NewNode(common()->IfSuccess(), value);
996         } else {
997           DCHECK(access_info.constant()->IsFunctionTemplateInfo());
998           Handle<FunctionTemplateInfo> function_template_info(
999               Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1000           DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1001           ZoneVector<Node*> stack_parameters(graph()->zone());
1002           ValueEffectControl value_effect_control = InlineApiCall(
1003               receiver, context, target, frame_state0, &stack_parameters,
1004               effect, control, shared_info, function_template_info);
1005           value = value_effect_control.value();
1006           effect = value_effect_control.effect();
1007           control = value_effect_control.control();
1008         }
1009         break;
1010       }
1011       case AccessMode::kStore: {
1012         // We need a FrameState for the setter stub to restore the correct
1013         // context and return the appropriate value to fullcodegen.
1014         FrameStateFunctionInfo const* frame_info0 =
1015             common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub,
1016                                                    2, 0, shared_info);
1017         Node* frame_state0 = graph()->NewNode(
1018             common()->FrameState(BailoutId::None(),
1019                                  OutputFrameStateCombine::Ignore(),
1020                                  frame_info0),
1021             graph()->NewNode(common()->StateValues(2), receiver, value),
1022             jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
1023             context, target, frame_state);
1024 
1025         // Introduce the call to the setter function.
1026         if (access_info.constant()->IsJSFunction()) {
1027           effect = graph()->NewNode(
1028               javascript()->CallFunction(
1029                   3, 0.0f, VectorSlotPair(),
1030                   ConvertReceiverMode::kNotNullOrUndefined),
1031               target, receiver, value, context, frame_state0, effect, control);
1032           control = graph()->NewNode(common()->IfSuccess(), effect);
1033         } else {
1034           DCHECK(access_info.constant()->IsFunctionTemplateInfo());
1035           Handle<FunctionTemplateInfo> function_template_info(
1036               Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1037           DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1038           ZoneVector<Node*> stack_parameters(graph()->zone());
1039           stack_parameters.push_back(value);
1040           ValueEffectControl value_effect_control = InlineApiCall(
1041               receiver, context, target, frame_state0, &stack_parameters,
1042               effect, control, shared_info, function_template_info);
1043           value = value_effect_control.value();
1044           effect = value_effect_control.effect();
1045           control = value_effect_control.control();
1046         }
1047         break;
1048       }
1049     }
1050   } else if (access_info.IsDataField()) {
1051     FieldIndex const field_index = access_info.field_index();
1052     Type* const field_type = access_info.field_type();
1053     MachineRepresentation const field_representation =
1054         access_info.field_representation();
1055     if (access_mode == AccessMode::kLoad) {
1056       if (access_info.holder().ToHandle(&holder)) {
1057         receiver = jsgraph()->Constant(holder);
1058       }
1059       // Optimize immutable property loads.
1060       HeapObjectMatcher m(receiver);
1061       if (m.HasValue() && m.Value()->IsJSObject()) {
1062         // TODO(turbofan): Given that we already have the field_index here, we
1063         // might be smarter in the future and not rely on the LookupIterator,
1064         // but for now let's just do what Crankshaft does.
1065         LookupIterator it(m.Value(), name,
1066                           LookupIterator::OWN_SKIP_INTERCEPTOR);
1067         if (it.IsFound() && it.IsReadOnly() && !it.IsConfigurable()) {
1068           Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
1069           return ValueEffectControl(value, effect, control);
1070         }
1071       }
1072     }
1073     Node* storage = receiver;
1074     if (!field_index.is_inobject()) {
1075       storage = effect = graph()->NewNode(
1076           simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
1077           storage, effect, control);
1078     }
1079     FieldAccess field_access = {
1080         kTaggedBase,
1081         field_index.offset(),
1082         name,
1083         field_type,
1084         MachineType::TypeForRepresentation(field_representation),
1085         kFullWriteBarrier};
1086     if (access_mode == AccessMode::kLoad) {
1087       if (field_representation == MachineRepresentation::kFloat64) {
1088         if (!field_index.is_inobject() || field_index.is_hidden_field() ||
1089             !FLAG_unbox_double_fields) {
1090           FieldAccess const storage_access = {kTaggedBase,
1091                                               field_index.offset(),
1092                                               name,
1093                                               Type::OtherInternal(),
1094                                               MachineType::TaggedPointer(),
1095                                               kPointerWriteBarrier};
1096           storage = effect =
1097               graph()->NewNode(simplified()->LoadField(storage_access), storage,
1098                                effect, control);
1099           field_access.offset = HeapNumber::kValueOffset;
1100           field_access.name = MaybeHandle<Name>();
1101         }
1102       }
1103       // TODO(turbofan): Track the field_map (if any) on the {field_access} and
1104       // use it in LoadElimination to eliminate map checks.
1105       value = effect = graph()->NewNode(simplified()->LoadField(field_access),
1106                                         storage, effect, control);
1107     } else {
1108       DCHECK_EQ(AccessMode::kStore, access_mode);
1109       switch (field_representation) {
1110         case MachineRepresentation::kFloat64: {
1111           value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1112                                             effect, control);
1113           if (!field_index.is_inobject() || field_index.is_hidden_field() ||
1114               !FLAG_unbox_double_fields) {
1115             if (access_info.HasTransitionMap()) {
1116               // Allocate a MutableHeapNumber for the new property.
1117               effect = graph()->NewNode(
1118                   common()->BeginRegion(RegionObservability::kNotObservable),
1119                   effect);
1120               Node* box = effect = graph()->NewNode(
1121                   simplified()->Allocate(NOT_TENURED),
1122                   jsgraph()->Constant(HeapNumber::kSize), effect, control);
1123               effect = graph()->NewNode(
1124                   simplified()->StoreField(AccessBuilder::ForMap()), box,
1125                   jsgraph()->HeapConstant(factory()->mutable_heap_number_map()),
1126                   effect, control);
1127               effect = graph()->NewNode(
1128                   simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
1129                   box, value, effect, control);
1130               value = effect =
1131                   graph()->NewNode(common()->FinishRegion(), box, effect);
1132 
1133               field_access.type = Type::Any();
1134               field_access.machine_type = MachineType::TaggedPointer();
1135               field_access.write_barrier_kind = kPointerWriteBarrier;
1136             } else {
1137               // We just store directly to the MutableHeapNumber.
1138               FieldAccess const storage_access = {kTaggedBase,
1139                                                   field_index.offset(),
1140                                                   name,
1141                                                   Type::OtherInternal(),
1142                                                   MachineType::TaggedPointer(),
1143                                                   kPointerWriteBarrier};
1144               storage = effect =
1145                   graph()->NewNode(simplified()->LoadField(storage_access),
1146                                    storage, effect, control);
1147               field_access.offset = HeapNumber::kValueOffset;
1148               field_access.name = MaybeHandle<Name>();
1149               field_access.machine_type = MachineType::Float64();
1150             }
1151           }
1152           break;
1153         }
1154         case MachineRepresentation::kTaggedSigned: {
1155           value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
1156                                             effect, control);
1157           field_access.write_barrier_kind = kNoWriteBarrier;
1158           break;
1159         }
1160         case MachineRepresentation::kTaggedPointer: {
1161           // Ensure that {value} is a HeapObject.
1162           value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
1163                                             value, effect, control);
1164           Handle<Map> field_map;
1165           if (access_info.field_map().ToHandle(&field_map)) {
1166             // Emit a map check for the value.
1167             effect = graph()->NewNode(simplified()->CheckMaps(1), value,
1168                                       jsgraph()->HeapConstant(field_map),
1169                                       effect, control);
1170           }
1171           field_access.write_barrier_kind = kPointerWriteBarrier;
1172           break;
1173         }
1174         case MachineRepresentation::kTagged:
1175           break;
1176         case MachineRepresentation::kNone:
1177         case MachineRepresentation::kBit:
1178         case MachineRepresentation::kWord8:
1179         case MachineRepresentation::kWord16:
1180         case MachineRepresentation::kWord32:
1181         case MachineRepresentation::kWord64:
1182         case MachineRepresentation::kFloat32:
1183         case MachineRepresentation::kSimd128:
1184           UNREACHABLE();
1185           break;
1186       }
1187       Handle<Map> transition_map;
1188       if (access_info.transition_map().ToHandle(&transition_map)) {
1189         effect = graph()->NewNode(
1190             common()->BeginRegion(RegionObservability::kObservable), effect);
1191         effect = graph()->NewNode(
1192             simplified()->StoreField(AccessBuilder::ForMap()), receiver,
1193             jsgraph()->Constant(transition_map), effect, control);
1194       }
1195       effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
1196                                 value, effect, control);
1197       if (access_info.HasTransitionMap()) {
1198         effect = graph()->NewNode(common()->FinishRegion(),
1199                                   jsgraph()->UndefinedConstant(), effect);
1200       }
1201     }
1202   } else {
1203     DCHECK(access_info.IsGeneric());
1204     DCHECK_EQ(AccessMode::kStore, access_mode);
1205     DCHECK_EQ(FeedbackVectorSlotKind::STORE_IC, vector->GetKind(slot));
1206     Callable callable =
1207         CodeFactory::StoreICInOptimizedCode(isolate(), language_mode);
1208     const CallInterfaceDescriptor& descriptor = callable.descriptor();
1209     CallDescriptor* desc = Linkage::GetStubCallDescriptor(
1210         isolate(), graph()->zone(), descriptor,
1211         descriptor.GetStackParameterCount(), CallDescriptor::kNeedsFrameState,
1212         Operator::kNoProperties);
1213     Node* stub_code = jsgraph()->HeapConstant(callable.code());
1214     Node* name_node = jsgraph()->HeapConstant(name);
1215     Node* slot_node = jsgraph()->Constant(vector->GetIndex(slot));
1216     Node* vector_node = jsgraph()->HeapConstant(vector);
1217 
1218     Node* inputs[] = {stub_code,   receiver, name_node,   value,  slot_node,
1219                       vector_node, context,  frame_state, effect, control};
1220 
1221     value = effect = control =
1222         graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
1223     control = graph()->NewNode(common()->IfSuccess(), control);
1224   }
1225 
1226   return ValueEffectControl(value, effect, control);
1227 }
1228 
1229 namespace {
1230 
GetArrayTypeFromElementsKind(ElementsKind kind)1231 ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
1232   switch (kind) {
1233 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
1234   case TYPE##_ELEMENTS:                                 \
1235     return kExternal##Type##Array;
1236     TYPED_ARRAYS(TYPED_ARRAY_CASE)
1237 #undef TYPED_ARRAY_CASE
1238     default:
1239       break;
1240   }
1241   UNREACHABLE();
1242   return kExternalInt8Array;
1243 }
1244 
1245 }  // namespace
1246 
1247 JSNativeContextSpecialization::ValueEffectControl
BuildElementAccess(Node * receiver,Node * index,Node * value,Node * effect,Node * control,ElementAccessInfo const & access_info,AccessMode access_mode,KeyedAccessStoreMode store_mode)1248 JSNativeContextSpecialization::BuildElementAccess(
1249     Node* receiver, Node* index, Node* value, Node* effect, Node* control,
1250     ElementAccessInfo const& access_info, AccessMode access_mode,
1251     KeyedAccessStoreMode store_mode) {
1252   // TODO(bmeurer): We currently specialize based on elements kind. We should
1253   // also be able to properly support strings and other JSObjects here.
1254   ElementsKind elements_kind = access_info.elements_kind();
1255   MapList const& receiver_maps = access_info.receiver_maps();
1256 
1257   // Load the elements for the {receiver}.
1258   Node* elements = effect = graph()->NewNode(
1259       simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
1260       effect, control);
1261 
1262   // Don't try to store to a copy-on-write backing store.
1263   if (access_mode == AccessMode::kStore &&
1264       IsFastSmiOrObjectElementsKind(elements_kind) &&
1265       store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
1266     effect =
1267         graph()->NewNode(simplified()->CheckMaps(1), elements,
1268                          jsgraph()->FixedArrayMapConstant(), effect, control);
1269   }
1270 
1271   if (IsFixedTypedArrayElementsKind(elements_kind)) {
1272     // Load the {receiver}s length.
1273     Node* length = effect = graph()->NewNode(
1274         simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
1275         receiver, effect, control);
1276 
1277     // Check if the {receiver}s buffer was neutered.
1278     Node* buffer = effect = graph()->NewNode(
1279         simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
1280         receiver, effect, control);
1281     Node* check = effect = graph()->NewNode(
1282         simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
1283 
1284     // Default to zero if the {receiver}s buffer was neutered.
1285     length = graph()->NewNode(
1286         common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
1287         check, jsgraph()->ZeroConstant(), length);
1288 
1289     if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
1290       // Check that the {index} is a valid array index, we do the actual
1291       // bounds check below and just skip the store below if it's out of
1292       // bounds for the {receiver}.
1293       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1294                                         jsgraph()->Constant(Smi::kMaxValue),
1295                                         effect, control);
1296     } else {
1297       // Check that the {index} is in the valid range for the {receiver}.
1298       DCHECK_EQ(STANDARD_STORE, store_mode);
1299       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1300                                         length, effect, control);
1301     }
1302 
1303     // Load the base and external pointer for the {receiver}.
1304     Node* base_pointer = effect = graph()->NewNode(
1305         simplified()->LoadField(
1306             AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
1307         elements, effect, control);
1308     Node* external_pointer = effect = graph()->NewNode(
1309         simplified()->LoadField(
1310             AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
1311         elements, effect, control);
1312 
1313     // Access the actual element.
1314     ExternalArrayType external_array_type =
1315         GetArrayTypeFromElementsKind(elements_kind);
1316     switch (access_mode) {
1317       case AccessMode::kLoad: {
1318         value = effect = graph()->NewNode(
1319             simplified()->LoadTypedElement(external_array_type), buffer,
1320             base_pointer, external_pointer, index, effect, control);
1321         break;
1322       }
1323       case AccessMode::kStore: {
1324         // Ensure that the {value} is actually a Number.
1325         value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1326                                           effect, control);
1327 
1328         // Introduce the appropriate truncation for {value}. Currently we
1329         // only need to do this for ClamedUint8Array {receiver}s, as the
1330         // other truncations are implicit in the StoreTypedElement, but we
1331         // might want to change that at some point.
1332         if (external_array_type == kExternalUint8ClampedArray) {
1333           value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
1334         }
1335 
1336         // Check if we can skip the out-of-bounds store.
1337         if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
1338           Node* check =
1339               graph()->NewNode(simplified()->NumberLessThan(), index, length);
1340           Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1341                                           check, control);
1342 
1343           Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1344           Node* etrue = effect;
1345           {
1346             // Perform the actual store.
1347             etrue = graph()->NewNode(
1348                 simplified()->StoreTypedElement(external_array_type), buffer,
1349                 base_pointer, external_pointer, index, value, etrue, if_true);
1350           }
1351 
1352           Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1353           Node* efalse = effect;
1354           {
1355             // Just ignore the out-of-bounds write.
1356           }
1357 
1358           control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1359           effect =
1360               graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1361         } else {
1362           // Perform the actual store
1363           DCHECK_EQ(STANDARD_STORE, store_mode);
1364           effect = graph()->NewNode(
1365               simplified()->StoreTypedElement(external_array_type), buffer,
1366               base_pointer, external_pointer, index, value, effect, control);
1367         }
1368         break;
1369       }
1370     }
1371   } else {
1372     // Check if the {receiver} is a JSArray.
1373     bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps);
1374 
1375     // Load the length of the {receiver}.
1376     Node* length = effect =
1377         receiver_is_jsarray
1378             ? graph()->NewNode(
1379                   simplified()->LoadField(
1380                       AccessBuilder::ForJSArrayLength(elements_kind)),
1381                   receiver, effect, control)
1382             : graph()->NewNode(
1383                   simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
1384                   elements, effect, control);
1385 
1386     // Check if we might need to grow the {elements} backing store.
1387     if (IsGrowStoreMode(store_mode)) {
1388       DCHECK_EQ(AccessMode::kStore, access_mode);
1389 
1390       // Check that the {index} is a valid array index; the actual checking
1391       // happens below right before the element store.
1392       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1393                                         jsgraph()->Constant(Smi::kMaxValue),
1394                                         effect, control);
1395     } else {
1396       // Check that the {index} is in the valid range for the {receiver}.
1397       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1398                                         length, effect, control);
1399     }
1400 
1401     // Compute the element access.
1402     Type* element_type = Type::NonInternal();
1403     MachineType element_machine_type = MachineType::AnyTagged();
1404     if (IsFastDoubleElementsKind(elements_kind)) {
1405       element_type = Type::Number();
1406       element_machine_type = MachineType::Float64();
1407     } else if (IsFastSmiElementsKind(elements_kind)) {
1408       element_type = Type::SignedSmall();
1409       element_machine_type = MachineType::TaggedSigned();
1410     }
1411     ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
1412                                     element_type, element_machine_type,
1413                                     kFullWriteBarrier};
1414 
1415     // Access the actual element.
1416     if (access_mode == AccessMode::kLoad) {
1417       // Compute the real element access type, which includes the hole in case
1418       // of holey backing stores.
1419       if (elements_kind == FAST_HOLEY_ELEMENTS ||
1420           elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
1421         element_access.type =
1422             Type::Union(element_type, Type::Hole(), graph()->zone());
1423         element_access.machine_type = MachineType::AnyTagged();
1424       }
1425       // Perform the actual backing store access.
1426       value = effect =
1427           graph()->NewNode(simplified()->LoadElement(element_access), elements,
1428                            index, effect, control);
1429       // Handle loading from holey backing stores correctly, by either mapping
1430       // the hole to undefined if possible, or deoptimizing otherwise.
1431       if (elements_kind == FAST_HOLEY_ELEMENTS ||
1432           elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
1433         // Check if we are allowed to turn the hole into undefined.
1434         if (CanTreatHoleAsUndefined(receiver_maps)) {
1435           // Turn the hole into undefined.
1436           value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
1437                                    value);
1438         } else {
1439           // Bailout if we see the hole.
1440           value = effect = graph()->NewNode(simplified()->CheckTaggedHole(),
1441                                             value, effect, control);
1442         }
1443       } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
1444         // Perform the hole check on the result.
1445         CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
1446         // Check if we are allowed to return the hole directly.
1447         if (CanTreatHoleAsUndefined(receiver_maps)) {
1448           // Return the signaling NaN hole directly if all uses are truncating.
1449           mode = CheckFloat64HoleMode::kAllowReturnHole;
1450         }
1451         value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
1452                                           value, effect, control);
1453       }
1454     } else {
1455       DCHECK_EQ(AccessMode::kStore, access_mode);
1456       if (IsFastSmiElementsKind(elements_kind)) {
1457         value = effect =
1458             graph()->NewNode(simplified()->CheckSmi(), value, effect, control);
1459       } else if (IsFastDoubleElementsKind(elements_kind)) {
1460         value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1461                                           effect, control);
1462         // Make sure we do not store signalling NaNs into double arrays.
1463         value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
1464       }
1465 
1466       // Ensure that copy-on-write backing store is writable.
1467       if (IsFastSmiOrObjectElementsKind(elements_kind) &&
1468           store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
1469         elements = effect =
1470             graph()->NewNode(simplified()->EnsureWritableFastElements(),
1471                              receiver, elements, effect, control);
1472       } else if (IsGrowStoreMode(store_mode)) {
1473         // Grow {elements} backing store if necessary. Also updates the
1474         // "length" property for JSArray {receiver}s, hence there must
1475         // not be any other check after this operation, as the write
1476         // to the "length" property is observable.
1477         GrowFastElementsFlags flags = GrowFastElementsFlag::kNone;
1478         if (receiver_is_jsarray) {
1479           flags |= GrowFastElementsFlag::kArrayObject;
1480         }
1481         if (IsHoleyElementsKind(elements_kind)) {
1482           flags |= GrowFastElementsFlag::kHoleyElements;
1483         }
1484         if (IsFastDoubleElementsKind(elements_kind)) {
1485           flags |= GrowFastElementsFlag::kDoubleElements;
1486         }
1487         elements = effect = graph()->NewNode(
1488             simplified()->MaybeGrowFastElements(flags), receiver, elements,
1489             index, length, effect, control);
1490       }
1491 
1492       // Perform the actual element access.
1493       effect = graph()->NewNode(simplified()->StoreElement(element_access),
1494                                 elements, index, value, effect, control);
1495     }
1496   }
1497 
1498   return ValueEffectControl(value, effect, control);
1499 }
1500 
1501 JSNativeContextSpecialization::ValueEffectControl
InlineApiCall(Node * receiver,Node * context,Node * target,Node * frame_state,ZoneVector<Node * > * stack_parameters,Node * effect,Node * control,Handle<SharedFunctionInfo> shared_info,Handle<FunctionTemplateInfo> function_template_info)1502 JSNativeContextSpecialization::InlineApiCall(
1503     Node* receiver, Node* context, Node* target, Node* frame_state,
1504     ZoneVector<Node*>* stack_parameters, Node* effect, Node* control,
1505     Handle<SharedFunctionInfo> shared_info,
1506     Handle<FunctionTemplateInfo> function_template_info) {
1507   Handle<CallHandlerInfo> call_handler_info = handle(
1508       CallHandlerInfo::cast(function_template_info->call_code()), isolate());
1509   Handle<Object> call_data_object(call_handler_info->data(), isolate());
1510 
1511   // The stub always expects the receiver as the first param on the stack.
1512   CallApiCallbackStub stub(
1513       isolate(), static_cast<int>(stack_parameters->size()),
1514       call_data_object->IsUndefined(isolate()),
1515       true /* TODO(epertoso): similar to CallOptimization */);
1516   CallInterfaceDescriptor call_interface_descriptor =
1517       stub.GetCallInterfaceDescriptor();
1518   CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
1519       isolate(), graph()->zone(), call_interface_descriptor,
1520       call_interface_descriptor.GetStackParameterCount() +
1521           static_cast<int>(stack_parameters->size()) + 1,
1522       CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
1523       MachineType::AnyTagged(), 1);
1524 
1525   Node* data = jsgraph()->Constant(call_data_object);
1526   ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
1527   Node* function_reference =
1528       graph()->NewNode(common()->ExternalConstant(ExternalReference(
1529           &function, ExternalReference::DIRECT_API_CALL, isolate())));
1530   Node* code = jsgraph()->HeapConstant(stub.GetCode());
1531 
1532   ZoneVector<Node*> inputs(zone());
1533   inputs.push_back(code);
1534 
1535   // CallApiCallbackStub's register arguments.
1536   inputs.push_back(target);
1537   inputs.push_back(data);
1538   inputs.push_back(receiver);
1539   inputs.push_back(function_reference);
1540 
1541   // Stack parameters: CallApiCallbackStub expects the first one to be the
1542   // receiver.
1543   inputs.push_back(receiver);
1544   for (Node* node : *stack_parameters) {
1545     inputs.push_back(node);
1546   }
1547   inputs.push_back(context);
1548   inputs.push_back(frame_state);
1549   inputs.push_back(effect);
1550   inputs.push_back(control);
1551 
1552   Node* effect0;
1553   Node* value0 = effect0 =
1554       graph()->NewNode(common()->Call(call_descriptor),
1555                        static_cast<int>(inputs.size()), inputs.data());
1556   Node* control0 = graph()->NewNode(common()->IfSuccess(), value0);
1557   return ValueEffectControl(value0, effect0, control0);
1558 }
1559 
BuildCheckMaps(Node * receiver,Node * effect,Node * control,std::vector<Handle<Map>> const & maps)1560 Node* JSNativeContextSpecialization::BuildCheckMaps(
1561     Node* receiver, Node* effect, Node* control,
1562     std::vector<Handle<Map>> const& maps) {
1563   HeapObjectMatcher m(receiver);
1564   if (m.HasValue()) {
1565     Handle<Map> receiver_map(m.Value()->map(), isolate());
1566     if (receiver_map->is_stable()) {
1567       for (Handle<Map> map : maps) {
1568         if (map.is_identical_to(receiver_map)) {
1569           dependencies()->AssumeMapStable(receiver_map);
1570           return effect;
1571         }
1572       }
1573     }
1574   }
1575   int const map_input_count = static_cast<int>(maps.size());
1576   int const input_count = 1 + map_input_count + 1 + 1;
1577   Node** inputs = zone()->NewArray<Node*>(input_count);
1578   inputs[0] = receiver;
1579   for (int i = 0; i < map_input_count; ++i) {
1580     inputs[1 + i] = jsgraph()->HeapConstant(maps[i]);
1581   }
1582   inputs[input_count - 2] = effect;
1583   inputs[input_count - 1] = control;
1584   return graph()->NewNode(simplified()->CheckMaps(map_input_count), input_count,
1585                           inputs);
1586 }
1587 
AssumePrototypesStable(std::vector<Handle<Map>> const & receiver_maps,Handle<JSObject> holder)1588 void JSNativeContextSpecialization::AssumePrototypesStable(
1589     std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
1590   // Determine actual holder and perform prototype chain checks.
1591   for (auto map : receiver_maps) {
1592     // Perform the implicit ToObject for primitives here.
1593     // Implemented according to ES6 section 7.3.2 GetV (V, P).
1594     Handle<JSFunction> constructor;
1595     if (Map::GetConstructorFunction(map, native_context())
1596             .ToHandle(&constructor)) {
1597       map = handle(constructor->initial_map(), isolate());
1598     }
1599     dependencies()->AssumePrototypeMapsStable(map, holder);
1600   }
1601 }
1602 
CanTreatHoleAsUndefined(std::vector<Handle<Map>> const & receiver_maps)1603 bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
1604     std::vector<Handle<Map>> const& receiver_maps) {
1605   // Check if the array prototype chain is intact.
1606   if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false;
1607 
1608   // Make sure both the initial Array and Object prototypes are stable.
1609   Handle<JSObject> initial_array_prototype(
1610       native_context()->initial_array_prototype(), isolate());
1611   Handle<JSObject> initial_object_prototype(
1612       native_context()->initial_object_prototype(), isolate());
1613   if (!initial_array_prototype->map()->is_stable() ||
1614       !initial_object_prototype->map()->is_stable()) {
1615     return false;
1616   }
1617 
1618   // Check if all {receiver_maps} either have the initial Array.prototype
1619   // or the initial Object.prototype as their prototype, as those are
1620   // guarded by the array protector cell.
1621   for (Handle<Map> map : receiver_maps) {
1622     if (map->prototype() != *initial_array_prototype &&
1623         map->prototype() != *initial_object_prototype) {
1624       return false;
1625     }
1626   }
1627 
1628   // Install code dependencies on the prototype maps.
1629   for (Handle<Map> map : receiver_maps) {
1630     dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype);
1631   }
1632 
1633   // Install code dependency on the array protector cell.
1634   dependencies()->AssumePropertyCell(factory()->array_protector());
1635   return true;
1636 }
1637 
ExtractReceiverMaps(Node * receiver,Node * effect,FeedbackNexus const & nexus,MapHandleList * receiver_maps)1638 bool JSNativeContextSpecialization::ExtractReceiverMaps(
1639     Node* receiver, Node* effect, FeedbackNexus const& nexus,
1640     MapHandleList* receiver_maps) {
1641   DCHECK_EQ(0, receiver_maps->length());
1642   // See if we can infer a concrete type for the {receiver}.
1643   Handle<Map> receiver_map;
1644   if (InferReceiverMap(receiver, effect).ToHandle(&receiver_map)) {
1645     // We can assume that the {receiver} still has the infered {receiver_map}.
1646     receiver_maps->Add(receiver_map);
1647     return true;
1648   }
1649   // Try to extract some maps from the {nexus}.
1650   if (nexus.ExtractMaps(receiver_maps) != 0) {
1651     // Try to filter impossible candidates based on infered root map.
1652     if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) {
1653       for (int i = receiver_maps->length(); --i >= 0;) {
1654         if (receiver_maps->at(i)->FindRootMap() != *receiver_map) {
1655           receiver_maps->Remove(i);
1656         }
1657       }
1658     }
1659     return true;
1660   }
1661   return false;
1662 }
1663 
InferReceiverMap(Node * receiver,Node * effect)1664 MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverMap(Node* receiver,
1665                                                                  Node* effect) {
1666   HeapObjectMatcher m(receiver);
1667   if (m.HasValue()) {
1668     Handle<Map> receiver_map(m.Value()->map(), isolate());
1669     if (receiver_map->is_stable()) return receiver_map;
1670   } else if (m.IsJSCreate()) {
1671     HeapObjectMatcher mtarget(m.InputAt(0));
1672     HeapObjectMatcher mnewtarget(m.InputAt(1));
1673     if (mtarget.HasValue() && mnewtarget.HasValue()) {
1674       Handle<JSFunction> constructor =
1675           Handle<JSFunction>::cast(mtarget.Value());
1676       if (constructor->has_initial_map()) {
1677         Handle<Map> initial_map(constructor->initial_map(), isolate());
1678         if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) {
1679           // Walk up the {effect} chain to see if the {receiver} is the
1680           // dominating effect and there's no other observable write in
1681           // between.
1682           while (true) {
1683             if (receiver == effect) return initial_map;
1684             if (!effect->op()->HasProperty(Operator::kNoWrite) ||
1685                 effect->op()->EffectInputCount() != 1) {
1686               break;
1687             }
1688             effect = NodeProperties::GetEffectInput(effect);
1689           }
1690         }
1691       }
1692     }
1693   }
1694   // TODO(turbofan): Go hunting for CheckMaps(receiver) in the effect chain?
1695   return MaybeHandle<Map>();
1696 }
1697 
InferReceiverRootMap(Node * receiver)1698 MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
1699     Node* receiver) {
1700   HeapObjectMatcher m(receiver);
1701   if (m.HasValue()) {
1702     return handle(m.Value()->map()->FindRootMap(), isolate());
1703   } else if (m.IsJSCreate()) {
1704     HeapObjectMatcher mtarget(m.InputAt(0));
1705     HeapObjectMatcher mnewtarget(m.InputAt(1));
1706     if (mtarget.HasValue() && mnewtarget.HasValue()) {
1707       Handle<JSFunction> constructor =
1708           Handle<JSFunction>::cast(mtarget.Value());
1709       if (constructor->has_initial_map()) {
1710         Handle<Map> initial_map(constructor->initial_map(), isolate());
1711         if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) {
1712           DCHECK_EQ(*initial_map, initial_map->FindRootMap());
1713           return initial_map;
1714         }
1715       }
1716     }
1717   }
1718   return MaybeHandle<Map>();
1719 }
1720 
graph() const1721 Graph* JSNativeContextSpecialization::graph() const {
1722   return jsgraph()->graph();
1723 }
1724 
isolate() const1725 Isolate* JSNativeContextSpecialization::isolate() const {
1726   return jsgraph()->isolate();
1727 }
1728 
factory() const1729 Factory* JSNativeContextSpecialization::factory() const {
1730   return isolate()->factory();
1731 }
1732 
machine() const1733 MachineOperatorBuilder* JSNativeContextSpecialization::machine() const {
1734   return jsgraph()->machine();
1735 }
1736 
common() const1737 CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
1738   return jsgraph()->common();
1739 }
1740 
javascript() const1741 JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
1742   return jsgraph()->javascript();
1743 }
1744 
simplified() const1745 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
1746   return jsgraph()->simplified();
1747 }
1748 
1749 }  // namespace compiler
1750 }  // namespace internal
1751 }  // namespace v8
1752