• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/feedback-vector.h"
18 #include "src/field-index-inl.h"
19 #include "src/isolate-inl.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 
58 struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
59   Handle<Context> context;
60   bool immutable;
61   int index;
62 };
63 
JSNativeContextSpecialization(Editor * editor,JSGraph * jsgraph,Flags flags,Handle<Context> native_context,CompilationDependencies * dependencies,Zone * zone)64 JSNativeContextSpecialization::JSNativeContextSpecialization(
65     Editor* editor, JSGraph* jsgraph, Flags flags,
66     Handle<Context> native_context, CompilationDependencies* dependencies,
67     Zone* zone)
68     : AdvancedReducer(editor),
69       jsgraph_(jsgraph),
70       flags_(flags),
71       global_object_(native_context->global_object()),
72       global_proxy_(JSGlobalProxy::cast(native_context->global_proxy())),
73       native_context_(native_context),
74       dependencies_(dependencies),
75       zone_(zone),
76       type_cache_(TypeCache::Get()) {}
77 
Reduce(Node * node)78 Reduction JSNativeContextSpecialization::Reduce(Node* node) {
79   switch (node->opcode()) {
80     case IrOpcode::kJSAdd:
81       return ReduceJSAdd(node);
82     case IrOpcode::kJSGetSuperConstructor:
83       return ReduceJSGetSuperConstructor(node);
84     case IrOpcode::kJSInstanceOf:
85       return ReduceJSInstanceOf(node);
86     case IrOpcode::kJSOrdinaryHasInstance:
87       return ReduceJSOrdinaryHasInstance(node);
88     case IrOpcode::kJSLoadContext:
89       return ReduceJSLoadContext(node);
90     case IrOpcode::kJSLoadGlobal:
91       return ReduceJSLoadGlobal(node);
92     case IrOpcode::kJSStoreGlobal:
93       return ReduceJSStoreGlobal(node);
94     case IrOpcode::kJSLoadNamed:
95       return ReduceJSLoadNamed(node);
96     case IrOpcode::kJSStoreNamed:
97       return ReduceJSStoreNamed(node);
98     case IrOpcode::kJSLoadProperty:
99       return ReduceJSLoadProperty(node);
100     case IrOpcode::kJSStoreProperty:
101       return ReduceJSStoreProperty(node);
102     case IrOpcode::kJSStoreNamedOwn:
103       return ReduceJSStoreNamedOwn(node);
104     case IrOpcode::kJSStoreDataPropertyInLiteral:
105       return ReduceJSStoreDataPropertyInLiteral(node);
106     default:
107       break;
108   }
109   return NoChange();
110 }
111 
ReduceJSAdd(Node * node)112 Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
113   // TODO(turbofan): This has to run together with the inlining and
114   // native context specialization to be able to leverage the string
115   // constant-folding for optimizing property access, but we should
116   // nevertheless find a better home for this at some point.
117   DCHECK_EQ(IrOpcode::kJSAdd, node->opcode());
118 
119   // Constant-fold string concatenation.
120   HeapObjectBinopMatcher m(node);
121   if (m.left().HasValue() && m.left().Value()->IsString() &&
122       m.right().HasValue() && m.right().Value()->IsString()) {
123     Handle<String> left = Handle<String>::cast(m.left().Value());
124     Handle<String> right = Handle<String>::cast(m.right().Value());
125     if (left->length() + right->length() <= String::kMaxLength) {
126       Handle<String> result =
127           factory()->NewConsString(left, right).ToHandleChecked();
128       Node* value = jsgraph()->HeapConstant(result);
129       ReplaceWithValue(node, value);
130       return Replace(value);
131     }
132   }
133   return NoChange();
134 }
135 
ReduceJSGetSuperConstructor(Node * node)136 Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
137     Node* node) {
138   DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode());
139   Node* constructor = NodeProperties::GetValueInput(node, 0);
140 
141   // If deoptimization is disabled, we cannot optimize.
142   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
143 
144   // Check if the input is a known JSFunction.
145   HeapObjectMatcher m(constructor);
146   if (!m.HasValue()) return NoChange();
147   Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
148   Handle<Map> function_map(function->map(), isolate());
149   Handle<Object> function_prototype(function_map->prototype(), isolate());
150 
151   // We can constant-fold the super constructor access if the
152   // {function}s map is stable, i.e. we can use a code dependency
153   // to guard against [[Prototype]] changes of {function}.
154   if (function_map->is_stable()) {
155     Node* value = jsgraph()->Constant(function_prototype);
156     dependencies()->AssumeMapStable(function_map);
157     if (function_prototype->IsConstructor()) {
158       ReplaceWithValue(node, value);
159       return Replace(value);
160     } else {
161       node->InsertInput(graph()->zone(), 0, value);
162       NodeProperties::ChangeOp(
163           node, javascript()->CallRuntime(Runtime::kThrowNotSuperConstructor));
164       return Changed(node);
165     }
166   }
167 
168   return NoChange();
169 }
170 
ReduceJSInstanceOf(Node * node)171 Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
172   DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
173   Node* object = NodeProperties::GetValueInput(node, 0);
174   Node* constructor = NodeProperties::GetValueInput(node, 1);
175   Node* context = NodeProperties::GetContextInput(node);
176   Node* effect = NodeProperties::GetEffectInput(node);
177   Node* control = NodeProperties::GetControlInput(node);
178 
179   // If deoptimization is disabled, we cannot optimize.
180   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
181 
182   // Check if the right hand side is a known {receiver}.
183   HeapObjectMatcher m(constructor);
184   if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange();
185   Handle<JSObject> receiver = Handle<JSObject>::cast(m.Value());
186   Handle<Map> receiver_map(receiver->map(), isolate());
187 
188   // Compute property access info for @@hasInstance on {receiver}.
189   PropertyAccessInfo access_info;
190   AccessInfoFactory access_info_factory(dependencies(), native_context(),
191                                         graph()->zone());
192   if (!access_info_factory.ComputePropertyAccessInfo(
193           receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad,
194           &access_info)) {
195     return NoChange();
196   }
197 
198   if (access_info.IsNotFound()) {
199     // If there's no @@hasInstance handler, the OrdinaryHasInstance operation
200     // takes over, but that requires the {receiver} to be callable.
201     if (receiver->IsCallable()) {
202       // Determine actual holder and perform prototype chain checks.
203       Handle<JSObject> holder;
204       if (access_info.holder().ToHandle(&holder)) {
205         AssumePrototypesStable(access_info.receiver_maps(), holder);
206       }
207 
208       // Monomorphic property access.
209       effect = BuildCheckMaps(constructor, effect, control,
210                               access_info.receiver_maps());
211 
212       // Lower to OrdinaryHasInstance(C, O).
213       NodeProperties::ReplaceValueInput(node, constructor, 0);
214       NodeProperties::ReplaceValueInput(node, object, 1);
215       NodeProperties::ReplaceEffectInput(node, effect);
216       NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
217       Reduction const reduction = ReduceJSOrdinaryHasInstance(node);
218       return reduction.Changed() ? reduction : Changed(node);
219     }
220   } else if (access_info.IsDataConstant()) {
221     DCHECK(access_info.constant()->IsCallable());
222 
223     // Determine actual holder and perform prototype chain checks.
224     Handle<JSObject> holder;
225     if (access_info.holder().ToHandle(&holder)) {
226       AssumePrototypesStable(access_info.receiver_maps(), holder);
227     }
228 
229     // Monomorphic property access.
230     effect = BuildCheckMaps(constructor, effect, control,
231                             access_info.receiver_maps());
232 
233     // Call the @@hasInstance handler.
234     Node* target = jsgraph()->Constant(access_info.constant());
235     node->InsertInput(graph()->zone(), 0, target);
236     node->ReplaceInput(1, constructor);
237     node->ReplaceInput(2, object);
238     node->ReplaceInput(5, effect);
239     NodeProperties::ChangeOp(
240         node,
241         javascript()->Call(3, 0.0f, VectorSlotPair(),
242                            ConvertReceiverMode::kNotNullOrUndefined));
243 
244     // Rewire the value uses of {node} to ToBoolean conversion of the result.
245     Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
246                                    node, context);
247     for (Edge edge : node->use_edges()) {
248       if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
249         edge.UpdateTo(value);
250         Revisit(edge.from());
251       }
252     }
253     return Changed(node);
254   }
255 
256   return NoChange();
257 }
258 
ReduceJSOrdinaryHasInstance(Node * node)259 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
260     Node* node) {
261   DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
262   Node* constructor = NodeProperties::GetValueInput(node, 0);
263   Node* object = NodeProperties::GetValueInput(node, 1);
264 
265   // Check if the {constructor} is a JSBoundFunction.
266   HeapObjectMatcher m(constructor);
267   if (m.HasValue() && m.Value()->IsJSBoundFunction()) {
268     // OrdinaryHasInstance on bound functions turns into a recursive
269     // invocation of the instanceof operator again.
270     // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2.
271     Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value());
272     Handle<JSReceiver> bound_target_function(function->bound_target_function());
273     NodeProperties::ReplaceValueInput(node, object, 0);
274     NodeProperties::ReplaceValueInput(
275         node, jsgraph()->HeapConstant(bound_target_function), 1);
276     NodeProperties::ChangeOp(node, javascript()->InstanceOf());
277     Reduction const reduction = ReduceJSInstanceOf(node);
278     return reduction.Changed() ? reduction : Changed(node);
279   }
280 
281   return NoChange();
282 }
283 
ReduceJSLoadContext(Node * node)284 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
285   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
286   ContextAccess const& access = ContextAccessOf(node->op());
287   // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native
288   // context (if any), so we can constant-fold those fields, which is
289   // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable.
290   if (access.index() == Context::NATIVE_CONTEXT_INDEX) {
291     Node* value = jsgraph()->HeapConstant(native_context());
292     ReplaceWithValue(node, value);
293     return Replace(value);
294   }
295   return NoChange();
296 }
297 
298 namespace {
299 
ForPropertyCellValue(MachineRepresentation representation,Type * type,MaybeHandle<Map> map,Handle<Name> name)300 FieldAccess ForPropertyCellValue(MachineRepresentation representation,
301                                  Type* type, MaybeHandle<Map> map,
302                                  Handle<Name> name) {
303   WriteBarrierKind kind = kFullWriteBarrier;
304   if (representation == MachineRepresentation::kTaggedSigned) {
305     kind = kNoWriteBarrier;
306   } else if (representation == MachineRepresentation::kTaggedPointer) {
307     kind = kPointerWriteBarrier;
308   }
309   MachineType r = MachineType::TypeForRepresentation(representation);
310   FieldAccess access = {
311       kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind};
312   return access;
313 }
314 
315 }  // namespace
316 
ReduceGlobalAccess(Node * node,Node * receiver,Node * value,Handle<Name> name,AccessMode access_mode,Node * index)317 Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
318     Node* node, Node* receiver, Node* value, Handle<Name> name,
319     AccessMode access_mode, Node* index) {
320   Node* effect = NodeProperties::GetEffectInput(node);
321   Node* control = NodeProperties::GetControlInput(node);
322 
323   // Lookup on the global object. We only deal with own data properties
324   // of the global object here (represented as PropertyCell).
325   LookupIterator it(global_object(), name, LookupIterator::OWN);
326   it.TryLookupCachedProperty();
327   if (it.state() != LookupIterator::DATA) return NoChange();
328   if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
329   Handle<PropertyCell> property_cell = it.GetPropertyCell();
330   PropertyDetails property_details = property_cell->property_details();
331   Handle<Object> property_cell_value(property_cell->value(), isolate());
332   PropertyCellType property_cell_type = property_details.cell_type();
333 
334   // We have additional constraints for stores.
335   if (access_mode == AccessMode::kStore) {
336     if (property_details.IsReadOnly()) {
337       // Don't even bother trying to lower stores to read-only data properties.
338       return NoChange();
339     } else if (property_cell_type == PropertyCellType::kUndefined) {
340       // There's no fast-path for dealing with undefined property cells.
341       return NoChange();
342     } else if (property_cell_type == PropertyCellType::kConstantType) {
343       // There's also no fast-path to store to a global cell which pretended
344       // to be stable, but is no longer stable now.
345       if (property_cell_value->IsHeapObject() &&
346           !Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) {
347         return NoChange();
348       }
349     }
350   }
351 
352   // Ensure that {index} matches the specified {name} (if {index} is given).
353   if (index != nullptr) {
354     Node* check = graph()->NewNode(simplified()->ReferenceEqual(), index,
355                                    jsgraph()->HeapConstant(name));
356     effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
357   }
358 
359   // Check if we have a {receiver} to validate. If so, we need to check that
360   // the {receiver} is actually the JSGlobalProxy for the native context that
361   // we are specializing to.
362   if (receiver != nullptr) {
363     Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver,
364                                    jsgraph()->HeapConstant(global_proxy()));
365     effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
366   }
367 
368   if (access_mode == AccessMode::kLoad) {
369     // Load from non-configurable, read-only data property on the global
370     // object can be constant-folded, even without deoptimization support.
371     if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
372       value = jsgraph()->Constant(property_cell_value);
373     } else {
374       // Record a code dependency on the cell if we can benefit from the
375       // additional feedback, or the global property is configurable (i.e.
376       // can be deleted or reconfigured to an accessor property).
377       if (property_details.cell_type() != PropertyCellType::kMutable ||
378           property_details.IsConfigurable()) {
379         dependencies()->AssumePropertyCell(property_cell);
380       }
381 
382       // Load from constant/undefined global property can be constant-folded.
383       if (property_details.cell_type() == PropertyCellType::kConstant ||
384           property_details.cell_type() == PropertyCellType::kUndefined) {
385         value = jsgraph()->Constant(property_cell_value);
386       } else {
387         // Load from constant type cell can benefit from type feedback.
388         MaybeHandle<Map> map;
389         Type* property_cell_value_type = Type::NonInternal();
390         MachineRepresentation representation = MachineRepresentation::kTagged;
391         if (property_details.cell_type() == PropertyCellType::kConstantType) {
392           // Compute proper type based on the current value in the cell.
393           if (property_cell_value->IsSmi()) {
394             property_cell_value_type = Type::SignedSmall();
395             representation = MachineRepresentation::kTaggedSigned;
396           } else if (property_cell_value->IsNumber()) {
397             property_cell_value_type = Type::Number();
398             representation = MachineRepresentation::kTaggedPointer;
399           } else {
400             Handle<Map> property_cell_value_map(
401                 Handle<HeapObject>::cast(property_cell_value)->map(),
402                 isolate());
403             property_cell_value_type = Type::For(property_cell_value_map);
404             representation = MachineRepresentation::kTaggedPointer;
405 
406             // We can only use the property cell value map for map check
407             // elimination if it's stable, i.e. the HeapObject wasn't
408             // mutated without the cell state being updated.
409             if (property_cell_value_map->is_stable()) {
410               dependencies()->AssumeMapStable(property_cell_value_map);
411               map = property_cell_value_map;
412             }
413           }
414         }
415         value = effect = graph()->NewNode(
416             simplified()->LoadField(ForPropertyCellValue(
417                 representation, property_cell_value_type, map, name)),
418             jsgraph()->HeapConstant(property_cell), effect, control);
419       }
420     }
421   } else {
422     DCHECK_EQ(AccessMode::kStore, access_mode);
423     DCHECK(!property_details.IsReadOnly());
424     switch (property_details.cell_type()) {
425       case PropertyCellType::kUndefined: {
426         UNREACHABLE();
427         break;
428       }
429       case PropertyCellType::kConstant: {
430         // Record a code dependency on the cell, and just deoptimize if the new
431         // value doesn't match the previous value stored inside the cell.
432         dependencies()->AssumePropertyCell(property_cell);
433         Node* check =
434             graph()->NewNode(simplified()->ReferenceEqual(), value,
435                              jsgraph()->Constant(property_cell_value));
436         effect =
437             graph()->NewNode(simplified()->CheckIf(), check, effect, control);
438         break;
439       }
440       case PropertyCellType::kConstantType: {
441         // Record a code dependency on the cell, and just deoptimize if the new
442         // values' type doesn't match the type of the previous value in the
443         // cell.
444         dependencies()->AssumePropertyCell(property_cell);
445         Type* property_cell_value_type;
446         MachineRepresentation representation = MachineRepresentation::kTagged;
447         if (property_cell_value->IsHeapObject()) {
448           // We cannot do anything if the {property_cell_value}s map is no
449           // longer stable.
450           Handle<Map> property_cell_value_map(
451               Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
452           DCHECK(property_cell_value_map->is_stable());
453           dependencies()->AssumeMapStable(property_cell_value_map);
454 
455           // Check that the {value} is a HeapObject.
456           value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
457                                             value, effect, control);
458 
459           // Check {value} map agains the {property_cell} map.
460           effect =
461               graph()->NewNode(simplified()->CheckMaps(
462                                    CheckMapsFlag::kNone,
463                                    ZoneHandleSet<Map>(property_cell_value_map)),
464                                value, effect, control);
465           property_cell_value_type = Type::OtherInternal();
466           representation = MachineRepresentation::kTaggedPointer;
467         } else {
468           // Check that the {value} is a Smi.
469           value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
470                                             effect, control);
471           property_cell_value_type = Type::SignedSmall();
472           representation = MachineRepresentation::kTaggedSigned;
473         }
474         effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
475                                       representation, property_cell_value_type,
476                                       MaybeHandle<Map>(), name)),
477                                   jsgraph()->HeapConstant(property_cell), value,
478                                   effect, control);
479         break;
480       }
481       case PropertyCellType::kMutable: {
482         // Record a code dependency on the cell, and just deoptimize if the
483         // property ever becomes read-only.
484         dependencies()->AssumePropertyCell(property_cell);
485         effect = graph()->NewNode(
486             simplified()->StoreField(ForPropertyCellValue(
487                 MachineRepresentation::kTagged, Type::NonInternal(),
488                 MaybeHandle<Map>(), name)),
489             jsgraph()->HeapConstant(property_cell), value, effect, control);
490         break;
491       }
492     }
493   }
494 
495   ReplaceWithValue(node, value, effect, control);
496   return Replace(value);
497 }
498 
ReduceJSLoadGlobal(Node * node)499 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
500   DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
501   Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
502   Node* effect = NodeProperties::GetEffectInput(node);
503 
504   // Try to lookup the name on the script context table first (lexical scoping).
505   ScriptContextTableLookupResult result;
506   if (LookupInScriptContextTable(name, &result)) {
507     if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
508     Node* context = jsgraph()->HeapConstant(result.context);
509     Node* value = effect = graph()->NewNode(
510         javascript()->LoadContext(0, result.index, result.immutable), context,
511         effect);
512     ReplaceWithValue(node, value, effect);
513     return Replace(value);
514   }
515 
516   // Not much we can do if deoptimization support is disabled.
517   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
518 
519   // Lookup the {name} on the global object instead.
520   return ReduceGlobalAccess(node, nullptr, nullptr, name, AccessMode::kLoad);
521 }
522 
ReduceJSStoreGlobal(Node * node)523 Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
524   DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
525   Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
526   Node* value = NodeProperties::GetValueInput(node, 0);
527   Node* effect = NodeProperties::GetEffectInput(node);
528   Node* control = NodeProperties::GetControlInput(node);
529 
530   // Try to lookup the name on the script context table first (lexical scoping).
531   ScriptContextTableLookupResult result;
532   if (LookupInScriptContextTable(name, &result)) {
533     if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
534     if (result.immutable) return NoChange();
535     Node* context = jsgraph()->HeapConstant(result.context);
536     effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
537                               value, context, effect, control);
538     ReplaceWithValue(node, value, effect, control);
539     return Replace(value);
540   }
541 
542   // Not much we can do if deoptimization support is disabled.
543   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
544 
545   // Lookup the {name} on the global object instead.
546   return ReduceGlobalAccess(node, nullptr, value, name, AccessMode::kStore);
547 }
548 
ReduceNamedAccess(Node * node,Node * value,MapHandleList const & receiver_maps,Handle<Name> name,AccessMode access_mode,LanguageMode language_mode,Handle<FeedbackVector> vector,FeedbackSlot slot,Node * index)549 Reduction JSNativeContextSpecialization::ReduceNamedAccess(
550     Node* node, Node* value, MapHandleList const& receiver_maps,
551     Handle<Name> name, AccessMode access_mode, LanguageMode language_mode,
552     Handle<FeedbackVector> vector, FeedbackSlot slot, Node* index) {
553   DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
554          node->opcode() == IrOpcode::kJSStoreNamed ||
555          node->opcode() == IrOpcode::kJSLoadProperty ||
556          node->opcode() == IrOpcode::kJSStoreProperty ||
557          node->opcode() == IrOpcode::kJSStoreNamedOwn);
558   Node* receiver = NodeProperties::GetValueInput(node, 0);
559   Node* context = NodeProperties::GetContextInput(node);
560   Node* frame_state = NodeProperties::GetFrameStateInput(node);
561   Node* effect = NodeProperties::GetEffectInput(node);
562   Node* control = NodeProperties::GetControlInput(node);
563 
564   // Not much we can do if deoptimization support is disabled.
565   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
566 
567   // Check if we have an access o.x or o.x=v where o is the current
568   // native contexts' global proxy, and turn that into a direct access
569   // to the current native contexts' global object instead.
570   if (receiver_maps.length() == 1) {
571     Handle<Map> receiver_map = receiver_maps.first();
572     if (receiver_map->IsJSGlobalProxyMap()) {
573       Object* maybe_constructor = receiver_map->GetConstructor();
574       // Detached global proxies have |null| as their constructor.
575       if (maybe_constructor->IsJSFunction() &&
576           JSFunction::cast(maybe_constructor)->native_context() ==
577               *native_context()) {
578         return ReduceGlobalAccess(node, receiver, value, name, access_mode,
579                                   index);
580       }
581     }
582   }
583 
584   // Compute property access infos for the receiver maps.
585   AccessInfoFactory access_info_factory(dependencies(), native_context(),
586                                         graph()->zone());
587   ZoneVector<PropertyAccessInfo> access_infos(zone());
588   if (!access_info_factory.ComputePropertyAccessInfos(
589           receiver_maps, name, access_mode, &access_infos)) {
590     return NoChange();
591   }
592 
593   // TODO(turbofan): Add support for inlining into try blocks.
594   bool is_exceptional = NodeProperties::IsExceptionalCall(node);
595   for (const auto& access_info : access_infos) {
596     if (access_info.IsAccessorConstant()) {
597       // Accessor in try-blocks are not supported yet.
598       if (is_exceptional || !(flags() & kAccessorInliningEnabled)) {
599         return NoChange();
600       }
601     } else if (access_info.IsGeneric()) {
602       // We do not handle generic calls in try blocks.
603       if (is_exceptional) return NoChange();
604       // We only handle the generic store IC case.
605       if (!vector->IsStoreIC(slot)) {
606         return NoChange();
607       }
608     }
609   }
610 
611   // Nothing to do if we have no non-deprecated maps.
612   if (access_infos.empty()) {
613     return ReduceSoftDeoptimize(
614         node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
615   }
616 
617   // Ensure that {index} matches the specified {name} (if {index} is given).
618   if (index != nullptr) {
619     Node* check = graph()->NewNode(simplified()->ReferenceEqual(), index,
620                                    jsgraph()->HeapConstant(name));
621     effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
622   }
623 
624   // Check for the monomorphic cases.
625   if (access_infos.size() == 1) {
626     PropertyAccessInfo access_info = access_infos.front();
627     if (HasOnlyStringMaps(access_info.receiver_maps())) {
628       // Monormorphic string access (ignoring the fact that there are multiple
629       // String maps).
630       receiver = effect = graph()->NewNode(simplified()->CheckString(),
631                                            receiver, effect, control);
632     } else if (HasOnlyNumberMaps(access_info.receiver_maps())) {
633       // Monomorphic number access (we also deal with Smis here).
634       receiver = effect = graph()->NewNode(simplified()->CheckNumber(),
635                                            receiver, effect, control);
636     } else {
637       // Monomorphic property access.
638       receiver = BuildCheckHeapObject(receiver, &effect, control);
639       effect = BuildCheckMaps(receiver, effect, control,
640                               access_info.receiver_maps());
641     }
642 
643     // Generate the actual property access.
644     ValueEffectControl continuation = BuildPropertyAccess(
645         receiver, value, context, frame_state, effect, control, name,
646         access_info, access_mode, language_mode, vector, slot);
647     value = continuation.value();
648     effect = continuation.effect();
649     control = continuation.control();
650   } else {
651     // The final states for every polymorphic branch. We join them with
652     // Merge+Phi+EffectPhi at the bottom.
653     ZoneVector<Node*> values(zone());
654     ZoneVector<Node*> effects(zone());
655     ZoneVector<Node*> controls(zone());
656 
657     // Check if {receiver} may be a number.
658     bool receiverissmi_possible = false;
659     for (PropertyAccessInfo const& access_info : access_infos) {
660       if (HasNumberMaps(access_info.receiver_maps())) {
661         receiverissmi_possible = true;
662         break;
663       }
664     }
665 
666     // Ensure that {receiver} is a heap object.
667     Node* receiverissmi_control = nullptr;
668     Node* receiverissmi_effect = effect;
669     if (receiverissmi_possible) {
670       Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
671       Node* branch = graph()->NewNode(common()->Branch(), check, control);
672       control = graph()->NewNode(common()->IfFalse(), branch);
673       receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
674       receiverissmi_effect = effect;
675     } else {
676       receiver = BuildCheckHeapObject(receiver, &effect, control);
677     }
678 
679     // Load the {receiver} map. The resulting effect is the dominating effect
680     // for all (polymorphic) branches.
681     Node* receiver_map = effect =
682         graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
683                          receiver, effect, control);
684 
685     // Generate code for the various different property access patterns.
686     Node* fallthrough_control = control;
687     for (size_t j = 0; j < access_infos.size(); ++j) {
688       PropertyAccessInfo const& access_info = access_infos[j];
689       Node* this_value = value;
690       Node* this_receiver = receiver;
691       Node* this_effect = effect;
692       Node* this_control = fallthrough_control;
693 
694       // Perform map check on {receiver}.
695       MapList const& receiver_maps = access_info.receiver_maps();
696       {
697         // Emit a (sequence of) map checks for other {receiver}s.
698         ZoneVector<Node*> this_controls(zone());
699         ZoneVector<Node*> this_effects(zone());
700         if (j == access_infos.size() - 1) {
701           // Last map check on the fallthrough control path, do a
702           // conditional eager deoptimization exit here.
703           this_effect = BuildCheckMaps(receiver, this_effect, this_control,
704                                        receiver_maps);
705           this_effects.push_back(this_effect);
706           this_controls.push_back(fallthrough_control);
707           fallthrough_control = nullptr;
708         } else {
709           for (auto map : receiver_maps) {
710             Node* check =
711                 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
712                                  jsgraph()->Constant(map));
713             Node* branch = graph()->NewNode(common()->Branch(), check,
714                                             fallthrough_control);
715             fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
716             this_controls.push_back(
717                 graph()->NewNode(common()->IfTrue(), branch));
718             this_effects.push_back(this_effect);
719           }
720         }
721 
722         // The Number case requires special treatment to also deal with Smis.
723         if (HasNumberMaps(receiver_maps)) {
724           // Join this check with the "receiver is smi" check above.
725           DCHECK_NOT_NULL(receiverissmi_effect);
726           DCHECK_NOT_NULL(receiverissmi_control);
727           this_effects.push_back(receiverissmi_effect);
728           this_controls.push_back(receiverissmi_control);
729           receiverissmi_effect = receiverissmi_control = nullptr;
730         }
731 
732         // Create single chokepoint for the control.
733         int const this_control_count = static_cast<int>(this_controls.size());
734         if (this_control_count == 1) {
735           this_control = this_controls.front();
736           this_effect = this_effects.front();
737         } else {
738           this_control =
739               graph()->NewNode(common()->Merge(this_control_count),
740                                this_control_count, &this_controls.front());
741           this_effects.push_back(this_control);
742           this_effect =
743               graph()->NewNode(common()->EffectPhi(this_control_count),
744                                this_control_count + 1, &this_effects.front());
745         }
746       }
747 
748       // Generate the actual property access.
749       ValueEffectControl continuation =
750           BuildPropertyAccess(this_receiver, this_value, context, frame_state,
751                               this_effect, this_control, name, access_info,
752                               access_mode, language_mode, vector, slot);
753       values.push_back(continuation.value());
754       effects.push_back(continuation.effect());
755       controls.push_back(continuation.control());
756     }
757 
758     DCHECK_NULL(fallthrough_control);
759 
760     // Generate the final merge point for all (polymorphic) branches.
761     int const control_count = static_cast<int>(controls.size());
762     if (control_count == 0) {
763       value = effect = control = jsgraph()->Dead();
764     } else if (control_count == 1) {
765       value = values.front();
766       effect = effects.front();
767       control = controls.front();
768     } else {
769       control = graph()->NewNode(common()->Merge(control_count), control_count,
770                                  &controls.front());
771       values.push_back(control);
772       value = graph()->NewNode(
773           common()->Phi(MachineRepresentation::kTagged, control_count),
774           control_count + 1, &values.front());
775       effects.push_back(control);
776       effect = graph()->NewNode(common()->EffectPhi(control_count),
777                                 control_count + 1, &effects.front());
778     }
779   }
780   ReplaceWithValue(node, value, effect, control);
781   return Replace(value);
782 }
783 
ReduceNamedAccessFromNexus(Node * node,Node * value,FeedbackNexus const & nexus,Handle<Name> name,AccessMode access_mode,LanguageMode language_mode)784 Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
785     Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name,
786     AccessMode access_mode, LanguageMode language_mode) {
787   DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
788          node->opcode() == IrOpcode::kJSStoreNamed ||
789          node->opcode() == IrOpcode::kJSStoreNamedOwn);
790   Node* const receiver = NodeProperties::GetValueInput(node, 0);
791   Node* const effect = NodeProperties::GetEffectInput(node);
792 
793   if (flags() & kDeoptimizationEnabled) {
794     // Check if we are accessing the current native contexts' global proxy.
795     HeapObjectMatcher m(receiver);
796     if (m.HasValue() && m.Value().is_identical_to(global_proxy())) {
797       // Optimize accesses to the current native contexts' global proxy.
798       return ReduceGlobalAccess(node, nullptr, value, name, access_mode);
799     }
800   }
801 
802   // Check if the {nexus} reports type feedback for the IC.
803   if (nexus.IsUninitialized()) {
804     if ((flags() & kDeoptimizationEnabled) &&
805         (flags() & kBailoutOnUninitialized)) {
806       return ReduceSoftDeoptimize(
807           node,
808           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
809     }
810     return NoChange();
811   }
812 
813   // Extract receiver maps from the IC using the {nexus}.
814   MapHandleList receiver_maps;
815   if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
816     return NoChange();
817   } else if (receiver_maps.length() == 0) {
818     if ((flags() & kDeoptimizationEnabled) &&
819         (flags() & kBailoutOnUninitialized)) {
820       return ReduceSoftDeoptimize(
821           node,
822           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
823     }
824     return NoChange();
825   }
826 
827   // Try to lower the named access based on the {receiver_maps}.
828   return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
829                            language_mode, nexus.vector_handle(), nexus.slot());
830 }
831 
ReduceJSLoadNamed(Node * node)832 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
833   DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
834   NamedAccess const& p = NamedAccessOf(node->op());
835   Node* const receiver = NodeProperties::GetValueInput(node, 0);
836   Node* const value = jsgraph()->Dead();
837 
838   // Check if we have a constant receiver.
839   HeapObjectMatcher m(receiver);
840   if (m.HasValue()) {
841     if (m.Value()->IsJSFunction() &&
842         p.name().is_identical_to(factory()->prototype_string())) {
843       // Optimize "prototype" property of functions.
844       Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
845       if (function->has_initial_map()) {
846         // We need to add a code dependency on the initial map of the
847         // {function} in order to be notified about changes to the
848         // "prototype" of {function}, so it doesn't make sense to
849         // continue unless deoptimization is enabled.
850         if (flags() & kDeoptimizationEnabled) {
851           Handle<Map> initial_map(function->initial_map(), isolate());
852           dependencies()->AssumeInitialMapCantChange(initial_map);
853           Handle<Object> prototype(initial_map->prototype(), isolate());
854           Node* value = jsgraph()->Constant(prototype);
855           ReplaceWithValue(node, value);
856           return Replace(value);
857         }
858       }
859     } else if (m.Value()->IsString() &&
860                p.name().is_identical_to(factory()->length_string())) {
861       // Constant-fold "length" property on constant strings.
862       Handle<String> string = Handle<String>::cast(m.Value());
863       Node* value = jsgraph()->Constant(string->length());
864       ReplaceWithValue(node, value);
865       return Replace(value);
866     }
867   }
868 
869   // Extract receiver maps from the LOAD_IC using the LoadICNexus.
870   if (!p.feedback().IsValid()) return NoChange();
871   LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
872 
873   // Try to lower the named access based on the {receiver_maps}.
874   return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
875                                     AccessMode::kLoad, p.language_mode());
876 }
877 
878 
ReduceJSStoreNamed(Node * node)879 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
880   DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
881   NamedAccess const& p = NamedAccessOf(node->op());
882   Node* const value = NodeProperties::GetValueInput(node, 1);
883 
884   // Extract receiver maps from the STORE_IC using the StoreICNexus.
885   if (!p.feedback().IsValid()) return NoChange();
886   StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
887 
888   // Try to lower the named access based on the {receiver_maps}.
889   return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
890                                     AccessMode::kStore, p.language_mode());
891 }
892 
ReduceJSStoreNamedOwn(Node * node)893 Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) {
894   DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode());
895   StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op());
896   Node* const value = NodeProperties::GetValueInput(node, 1);
897 
898   // Extract receiver maps from the IC using the StoreOwnICNexus.
899   if (!p.feedback().IsValid()) return NoChange();
900   StoreOwnICNexus nexus(p.feedback().vector(), p.feedback().slot());
901 
902   // Try to lower the creation of a named property based on the {receiver_maps}.
903   return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
904                                     AccessMode::kStoreInLiteral, STRICT);
905 }
906 
ReduceElementAccess(Node * node,Node * index,Node * value,MapHandleList const & receiver_maps,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)907 Reduction JSNativeContextSpecialization::ReduceElementAccess(
908     Node* node, Node* index, Node* value, MapHandleList const& receiver_maps,
909     AccessMode access_mode, LanguageMode language_mode,
910     KeyedAccessStoreMode store_mode) {
911   DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
912          node->opcode() == IrOpcode::kJSStoreProperty);
913   Node* receiver = NodeProperties::GetValueInput(node, 0);
914   Node* effect = NodeProperties::GetEffectInput(node);
915   Node* control = NodeProperties::GetControlInput(node);
916   Node* frame_state = NodeProperties::FindFrameStateBefore(node);
917 
918   // Not much we can do if deoptimization support is disabled.
919   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
920 
921   // Check for keyed access to strings.
922   if (HasOnlyStringMaps(receiver_maps)) {
923     // Strings are immutable in JavaScript.
924     if (access_mode == AccessMode::kStore) return NoChange();
925 
926     // Ensure that the {receiver} is actually a String.
927     receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver,
928                                          effect, control);
929 
930     // Determine the {receiver} length.
931     Node* length = effect = graph()->NewNode(
932         simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
933         effect, control);
934 
935     // Ensure that {index} is less than {receiver} length.
936     index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
937                                       length, effect, control);
938 
939     // Return the character from the {receiver} as single character string.
940     value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
941                              control);
942   } else {
943     // Retrieve the native context from the given {node}.
944     // Compute element access infos for the receiver maps.
945     AccessInfoFactory access_info_factory(dependencies(), native_context(),
946                                           graph()->zone());
947     ZoneVector<ElementAccessInfo> access_infos(zone());
948     if (!access_info_factory.ComputeElementAccessInfos(
949             receiver_maps, access_mode, &access_infos)) {
950       return NoChange();
951     }
952 
953     // Nothing to do if we have no non-deprecated maps.
954     if (access_infos.empty()) {
955       return ReduceSoftDeoptimize(
956           node,
957           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
958     }
959 
960     // For holey stores or growing stores, we need to check that the prototype
961     // chain contains no setters for elements, and we need to guard those checks
962     // via code dependencies on the relevant prototype maps.
963     if (access_mode == AccessMode::kStore) {
964       // TODO(turbofan): We could have a fast path here, that checks for the
965       // common case of Array or Object prototype only and therefore avoids
966       // the zone allocation of this vector.
967       ZoneVector<Handle<Map>> prototype_maps(zone());
968       for (ElementAccessInfo const& access_info : access_infos) {
969         for (Handle<Map> receiver_map : access_info.receiver_maps()) {
970           // If the {receiver_map} has a prototype and it's elements backing
971           // store is either holey, or we have a potentially growing store,
972           // then we need to check that all prototypes have stable maps with
973           // fast elements (and we need to guard against changes to that below).
974           if (IsHoleyElementsKind(receiver_map->elements_kind()) ||
975               IsGrowStoreMode(store_mode)) {
976             // Make sure all prototypes are stable and have fast elements.
977             for (Handle<Map> map = receiver_map;;) {
978               Handle<Object> map_prototype(map->prototype(), isolate());
979               if (map_prototype->IsNull(isolate())) break;
980               if (!map_prototype->IsJSObject()) return NoChange();
981               map = handle(Handle<JSObject>::cast(map_prototype)->map(),
982                            isolate());
983               if (!map->is_stable()) return NoChange();
984               if (!IsFastElementsKind(map->elements_kind())) return NoChange();
985               prototype_maps.push_back(map);
986             }
987           }
988         }
989       }
990 
991       // Install dependencies on the relevant prototype maps.
992       for (Handle<Map> prototype_map : prototype_maps) {
993         dependencies()->AssumeMapStable(prototype_map);
994       }
995     }
996 
997     // Ensure that {receiver} is a heap object.
998     receiver = BuildCheckHeapObject(receiver, &effect, control);
999 
1000     // Check for the monomorphic case.
1001     if (access_infos.size() == 1) {
1002       ElementAccessInfo access_info = access_infos.front();
1003 
1004       // Perform possible elements kind transitions.
1005       for (auto transition : access_info.transitions()) {
1006         Handle<Map> const transition_source = transition.first;
1007         Handle<Map> const transition_target = transition.second;
1008         effect = graph()->NewNode(
1009             simplified()->TransitionElementsKind(ElementsTransition(
1010                 IsSimpleMapChangeTransition(transition_source->elements_kind(),
1011                                             transition_target->elements_kind())
1012                     ? ElementsTransition::kFastTransition
1013                     : ElementsTransition::kSlowTransition,
1014                 transition_source, transition_target)),
1015             receiver, effect, control);
1016       }
1017 
1018       // TODO(turbofan): The effect/control linearization will not find a
1019       // FrameState after the StoreField or Call that is generated for the
1020       // elements kind transition above. This is because those operators
1021       // don't have the kNoWrite flag on it, even though they are not
1022       // observable by JavaScript.
1023       effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect,
1024                                 control);
1025 
1026       // Perform map check on the {receiver}.
1027       effect = BuildCheckMaps(receiver, effect, control,
1028                               access_info.receiver_maps());
1029 
1030       // Access the actual element.
1031       ValueEffectControl continuation =
1032           BuildElementAccess(receiver, index, value, effect, control,
1033                              access_info, access_mode, store_mode);
1034       value = continuation.value();
1035       effect = continuation.effect();
1036       control = continuation.control();
1037     } else {
1038       // The final states for every polymorphic branch. We join them with
1039       // Merge+Phi+EffectPhi at the bottom.
1040       ZoneVector<Node*> values(zone());
1041       ZoneVector<Node*> effects(zone());
1042       ZoneVector<Node*> controls(zone());
1043 
1044       // Generate code for the various different element access patterns.
1045       Node* fallthrough_control = control;
1046       for (size_t j = 0; j < access_infos.size(); ++j) {
1047         ElementAccessInfo const& access_info = access_infos[j];
1048         Node* this_receiver = receiver;
1049         Node* this_value = value;
1050         Node* this_index = index;
1051         Node* this_effect = effect;
1052         Node* this_control = fallthrough_control;
1053 
1054         // Perform possible elements kind transitions.
1055         for (auto transition : access_info.transitions()) {
1056           Handle<Map> const transition_source = transition.first;
1057           Handle<Map> const transition_target = transition.second;
1058           this_effect = graph()->NewNode(
1059               simplified()->TransitionElementsKind(
1060                   ElementsTransition(IsSimpleMapChangeTransition(
1061                                          transition_source->elements_kind(),
1062                                          transition_target->elements_kind())
1063                                          ? ElementsTransition::kFastTransition
1064                                          : ElementsTransition::kSlowTransition,
1065                                      transition_source, transition_target)),
1066               receiver, this_effect, this_control);
1067         }
1068 
1069         // Load the {receiver} map.
1070         Node* receiver_map = this_effect =
1071             graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
1072                              receiver, this_effect, this_control);
1073 
1074         // Perform map check(s) on {receiver}.
1075         MapList const& receiver_maps = access_info.receiver_maps();
1076         if (j == access_infos.size() - 1) {
1077           // Last map check on the fallthrough control path, do a
1078           // conditional eager deoptimization exit here.
1079           this_effect = BuildCheckMaps(receiver, this_effect, this_control,
1080                                        receiver_maps);
1081           fallthrough_control = nullptr;
1082         } else {
1083           ZoneVector<Node*> this_controls(zone());
1084           ZoneVector<Node*> this_effects(zone());
1085           for (Handle<Map> map : receiver_maps) {
1086             Node* check =
1087                 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
1088                                  jsgraph()->Constant(map));
1089             Node* branch = graph()->NewNode(common()->Branch(), check,
1090                                             fallthrough_control);
1091             this_controls.push_back(
1092                 graph()->NewNode(common()->IfTrue(), branch));
1093             this_effects.push_back(this_effect);
1094             fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
1095           }
1096 
1097           // Create single chokepoint for the control.
1098           int const this_control_count = static_cast<int>(this_controls.size());
1099           if (this_control_count == 1) {
1100             this_control = this_controls.front();
1101             this_effect = this_effects.front();
1102           } else {
1103             this_control =
1104                 graph()->NewNode(common()->Merge(this_control_count),
1105                                  this_control_count, &this_controls.front());
1106             this_effects.push_back(this_control);
1107             this_effect =
1108                 graph()->NewNode(common()->EffectPhi(this_control_count),
1109                                  this_control_count + 1, &this_effects.front());
1110           }
1111         }
1112 
1113         // Access the actual element.
1114         ValueEffectControl continuation = BuildElementAccess(
1115             this_receiver, this_index, this_value, this_effect, this_control,
1116             access_info, access_mode, store_mode);
1117         values.push_back(continuation.value());
1118         effects.push_back(continuation.effect());
1119         controls.push_back(continuation.control());
1120       }
1121 
1122       DCHECK_NULL(fallthrough_control);
1123 
1124       // Generate the final merge point for all (polymorphic) branches.
1125       int const control_count = static_cast<int>(controls.size());
1126       if (control_count == 0) {
1127         value = effect = control = jsgraph()->Dead();
1128       } else if (control_count == 1) {
1129         value = values.front();
1130         effect = effects.front();
1131         control = controls.front();
1132       } else {
1133         control = graph()->NewNode(common()->Merge(control_count),
1134                                    control_count, &controls.front());
1135         values.push_back(control);
1136         value = graph()->NewNode(
1137             common()->Phi(MachineRepresentation::kTagged, control_count),
1138             control_count + 1, &values.front());
1139         effects.push_back(control);
1140         effect = graph()->NewNode(common()->EffectPhi(control_count),
1141                                   control_count + 1, &effects.front());
1142       }
1143     }
1144   }
1145 
1146   ReplaceWithValue(node, value, effect, control);
1147   return Replace(value);
1148 }
1149 
1150 template <typename KeyedICNexus>
ReduceKeyedAccess(Node * node,Node * index,Node * value,KeyedICNexus const & nexus,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)1151 Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
1152     Node* node, Node* index, Node* value, KeyedICNexus const& nexus,
1153     AccessMode access_mode, LanguageMode language_mode,
1154     KeyedAccessStoreMode store_mode) {
1155   DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1156          node->opcode() == IrOpcode::kJSStoreProperty);
1157   Node* receiver = NodeProperties::GetValueInput(node, 0);
1158   Node* effect = NodeProperties::GetEffectInput(node);
1159   Node* control = NodeProperties::GetControlInput(node);
1160 
1161   // Optimize access for constant {receiver}.
1162   HeapObjectMatcher mreceiver(receiver);
1163   if (mreceiver.HasValue() && mreceiver.Value()->IsString()) {
1164     Handle<String> string = Handle<String>::cast(mreceiver.Value());
1165 
1166     // We can only assume that the {index} is a valid array index if the IC
1167     // is in element access mode and not MEGAMORPHIC, otherwise there's no
1168     // guard for the bounds check below.
1169     if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) {
1170       // Strings are immutable in JavaScript.
1171       if (access_mode == AccessMode::kStore) return NoChange();
1172 
1173       // Properly deal with constant {index}.
1174       NumberMatcher mindex(index);
1175       if (mindex.IsInteger() && mindex.IsInRange(0.0, string->length() - 1)) {
1176         // Constant-fold the {index} access to {string}.
1177         Node* value = jsgraph()->HeapConstant(
1178             factory()->LookupSingleCharacterStringFromCode(
1179                 string->Get(static_cast<int>(mindex.Value()))));
1180         ReplaceWithValue(node, value, effect, control);
1181         return Replace(value);
1182       } else if (flags() & kDeoptimizationEnabled) {
1183         // Ensure that {index} is less than {receiver} length.
1184         Node* length = jsgraph()->Constant(string->length());
1185         index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1186                                           length, effect, control);
1187 
1188         // Return the character from the {receiver} as single character string.
1189         value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
1190                                  control);
1191         ReplaceWithValue(node, value, effect, control);
1192         return Replace(value);
1193       }
1194     }
1195   }
1196 
1197   // Check if the {nexus} reports type feedback for the IC.
1198   if (nexus.IsUninitialized()) {
1199     if ((flags() & kDeoptimizationEnabled) &&
1200         (flags() & kBailoutOnUninitialized)) {
1201       return ReduceSoftDeoptimize(
1202           node,
1203           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
1204     }
1205     return NoChange();
1206   }
1207 
1208   // Extract receiver maps from the {nexus}.
1209   MapHandleList receiver_maps;
1210   if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
1211     return NoChange();
1212   } else if (receiver_maps.length() == 0) {
1213     if ((flags() & kDeoptimizationEnabled) &&
1214         (flags() & kBailoutOnUninitialized)) {
1215       return ReduceSoftDeoptimize(
1216           node,
1217           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
1218     }
1219     return NoChange();
1220   }
1221 
1222   // Optimize access for constant {index}.
1223   HeapObjectMatcher mindex(index);
1224   if (mindex.HasValue() && mindex.Value()->IsPrimitive()) {
1225     // Keyed access requires a ToPropertyKey on the {index} first before
1226     // looking up the property on the object (see ES6 section 12.3.2.1).
1227     // We can only do this for non-observable ToPropertyKey invocations,
1228     // so we limit the constant indices to primitives at this point.
1229     Handle<Name> name;
1230     if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) {
1231       uint32_t array_index;
1232       if (name->AsArrayIndex(&array_index)) {
1233         // Use the constant array index.
1234         index = jsgraph()->Constant(static_cast<double>(array_index));
1235       } else {
1236         name = factory()->InternalizeName(name);
1237         return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
1238                                  language_mode, nexus.vector_handle(),
1239                                  nexus.slot());
1240       }
1241     }
1242   }
1243 
1244   // Check if we have feedback for a named access.
1245   if (Name* name = nexus.FindFirstName()) {
1246     return ReduceNamedAccess(
1247         node, value, receiver_maps, handle(name, isolate()), access_mode,
1248         language_mode, nexus.vector_handle(), nexus.slot(), index);
1249   } else if (nexus.GetKeyType() != ELEMENT) {
1250     // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume
1251     // that the {index} is a valid array index, thus we just let the IC continue
1252     // to deal with this load/store.
1253     return NoChange();
1254   } else if (nexus.ic_state() == MEGAMORPHIC) {
1255     // The KeyedLoad/StoreIC uses the MEGAMORPHIC state to guard the assumption
1256     // that a numeric {index} is within the valid bounds for {receiver}, i.e.
1257     // it transitions to MEGAMORPHIC once it sees an out-of-bounds access. Thus
1258     // we cannot continue here if the IC state is MEGAMORPHIC.
1259     return NoChange();
1260   }
1261 
1262   // Try to lower the element access based on the {receiver_maps}.
1263   return ReduceElementAccess(node, index, value, receiver_maps, access_mode,
1264                              language_mode, store_mode);
1265 }
1266 
ReduceSoftDeoptimize(Node * node,DeoptimizeReason reason)1267 Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
1268     Node* node, DeoptimizeReason reason) {
1269   Node* effect = NodeProperties::GetEffectInput(node);
1270   Node* control = NodeProperties::GetControlInput(node);
1271   Node* frame_state = NodeProperties::FindFrameStateBefore(node);
1272   Node* deoptimize =
1273       graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason),
1274                        frame_state, effect, control);
1275   // TODO(bmeurer): This should be on the AdvancedReducer somehow.
1276   NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
1277   Revisit(graph()->end());
1278   node->TrimInputCount(0);
1279   NodeProperties::ChangeOp(node, common()->Dead());
1280   return Changed(node);
1281 }
1282 
1283 
ReduceJSLoadProperty(Node * node)1284 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
1285   DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
1286   PropertyAccess const& p = PropertyAccessOf(node->op());
1287   Node* const index = NodeProperties::GetValueInput(node, 1);
1288   Node* const value = jsgraph()->Dead();
1289 
1290   // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus.
1291   if (!p.feedback().IsValid()) return NoChange();
1292   KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
1293 
1294   // Try to lower the keyed access based on the {nexus}.
1295   return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad,
1296                            p.language_mode(), STANDARD_STORE);
1297 }
1298 
1299 
ReduceJSStoreProperty(Node * node)1300 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
1301   DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode());
1302   PropertyAccess const& p = PropertyAccessOf(node->op());
1303   Node* const index = NodeProperties::GetValueInput(node, 1);
1304   Node* const value = NodeProperties::GetValueInput(node, 2);
1305 
1306   // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus.
1307   if (!p.feedback().IsValid()) return NoChange();
1308   KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
1309 
1310   // Extract the keyed access store mode from the KEYED_STORE_IC.
1311   KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode();
1312 
1313   // Try to lower the keyed access based on the {nexus}.
1314   return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore,
1315                            p.language_mode(), store_mode);
1316 }
1317 
1318 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<FeedbackVector> vector,FeedbackSlot slot)1319 JSNativeContextSpecialization::BuildPropertyAccess(
1320     Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
1321     Node* control, Handle<Name> name, PropertyAccessInfo const& access_info,
1322     AccessMode access_mode, LanguageMode language_mode,
1323     Handle<FeedbackVector> vector, FeedbackSlot slot) {
1324   // Determine actual holder and perform prototype chain checks.
1325   Handle<JSObject> holder;
1326   if (access_info.holder().ToHandle(&holder)) {
1327     DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
1328     AssumePrototypesStable(access_info.receiver_maps(), holder);
1329   }
1330 
1331   // Generate the actual property access.
1332   if (access_info.IsNotFound()) {
1333     DCHECK_EQ(AccessMode::kLoad, access_mode);
1334     value = jsgraph()->UndefinedConstant();
1335   } else if (access_info.IsDataConstant()) {
1336     Node* constant_value = jsgraph()->Constant(access_info.constant());
1337     if (access_mode == AccessMode::kStore) {
1338       Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value,
1339                                      constant_value);
1340       effect =
1341           graph()->NewNode(simplified()->CheckIf(), check, effect, control);
1342     }
1343     value = constant_value;
1344   } else if (access_info.IsAccessorConstant()) {
1345     // TODO(bmeurer): Properly rewire the IfException edge here if there's any.
1346     Node* target = jsgraph()->Constant(access_info.constant());
1347     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
1348     Handle<SharedFunctionInfo> shared_info =
1349         frame_info.shared_info().ToHandleChecked();
1350     switch (access_mode) {
1351       case AccessMode::kLoad: {
1352         // We need a FrameState for the getter stub to restore the correct
1353         // context before returning to fullcodegen.
1354         FrameStateFunctionInfo const* frame_info0 =
1355             common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub,
1356                                                    1, 0, shared_info);
1357         Node* frame_state0 = graph()->NewNode(
1358             common()->FrameState(BailoutId::None(),
1359                                  OutputFrameStateCombine::Ignore(),
1360                                  frame_info0),
1361             graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
1362                              receiver),
1363             jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
1364             context, target, frame_state);
1365 
1366         // Introduce the call to the getter function.
1367         if (access_info.constant()->IsJSFunction()) {
1368           value = effect = graph()->NewNode(
1369               javascript()->Call(2, 0.0f, VectorSlotPair(),
1370                                  ConvertReceiverMode::kNotNullOrUndefined),
1371               target, receiver, context, frame_state0, effect, control);
1372           control = graph()->NewNode(common()->IfSuccess(), value);
1373         } else {
1374           DCHECK(access_info.constant()->IsFunctionTemplateInfo());
1375           Handle<FunctionTemplateInfo> function_template_info(
1376               Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1377           DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1378           ValueEffectControl value_effect_control = InlineApiCall(
1379               receiver, context, target, frame_state0, nullptr, effect, control,
1380               shared_info, function_template_info);
1381           value = value_effect_control.value();
1382           effect = value_effect_control.effect();
1383           control = value_effect_control.control();
1384         }
1385         break;
1386       }
1387       case AccessMode::kStoreInLiteral:
1388       case AccessMode::kStore: {
1389         // We need a FrameState for the setter stub to restore the correct
1390         // context and return the appropriate value to fullcodegen.
1391         FrameStateFunctionInfo const* frame_info0 =
1392             common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub,
1393                                                    2, 0, shared_info);
1394         Node* frame_state0 = graph()->NewNode(
1395             common()->FrameState(BailoutId::None(),
1396                                  OutputFrameStateCombine::Ignore(),
1397                                  frame_info0),
1398             graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
1399                              receiver, value),
1400             jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
1401             context, target, frame_state);
1402 
1403         // Introduce the call to the setter function.
1404         if (access_info.constant()->IsJSFunction()) {
1405           effect = graph()->NewNode(
1406               javascript()->Call(3, 0.0f, VectorSlotPair(),
1407                                  ConvertReceiverMode::kNotNullOrUndefined),
1408               target, receiver, value, context, frame_state0, effect, control);
1409           control = graph()->NewNode(common()->IfSuccess(), effect);
1410         } else {
1411           DCHECK(access_info.constant()->IsFunctionTemplateInfo());
1412           Handle<FunctionTemplateInfo> function_template_info(
1413               Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1414           DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1415           ValueEffectControl value_effect_control = InlineApiCall(
1416               receiver, context, target, frame_state0, value, effect, control,
1417               shared_info, function_template_info);
1418           value = value_effect_control.value();
1419           effect = value_effect_control.effect();
1420           control = value_effect_control.control();
1421         }
1422         break;
1423       }
1424     }
1425   } else if (access_info.IsDataField() || access_info.IsDataConstantField()) {
1426     FieldIndex const field_index = access_info.field_index();
1427     Type* const field_type = access_info.field_type();
1428     MachineRepresentation const field_representation =
1429         access_info.field_representation();
1430     if (access_mode == AccessMode::kLoad) {
1431       if (access_info.holder().ToHandle(&holder)) {
1432         receiver = jsgraph()->Constant(holder);
1433       }
1434       // Optimize immutable property loads.
1435       HeapObjectMatcher m(receiver);
1436       if (m.HasValue() && m.Value()->IsJSObject()) {
1437         // TODO(ishell): Use something simpler like
1438         //
1439         // Handle<Object> value =
1440         //     JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
1441         //                              Representation::Tagged(), field_index);
1442         //
1443         // here, once we have the immutable bit in the access_info.
1444 
1445         // TODO(turbofan): Given that we already have the field_index here, we
1446         // might be smarter in the future and not rely on the LookupIterator,
1447         // but for now let's just do what Crankshaft does.
1448         LookupIterator it(m.Value(), name,
1449                           LookupIterator::OWN_SKIP_INTERCEPTOR);
1450         if (it.state() == LookupIterator::DATA) {
1451           bool is_reaonly_non_configurable =
1452               it.IsReadOnly() && !it.IsConfigurable();
1453           if (is_reaonly_non_configurable ||
1454               (FLAG_track_constant_fields &&
1455                access_info.IsDataConstantField())) {
1456             Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
1457             if (!is_reaonly_non_configurable) {
1458               // It's necessary to add dependency on the map that introduced
1459               // the field.
1460               DCHECK(access_info.IsDataConstantField());
1461               DCHECK(!it.is_dictionary_holder());
1462               Handle<Map> field_owner_map = it.GetFieldOwnerMap();
1463               dependencies()->AssumeFieldOwner(field_owner_map);
1464             }
1465             return ValueEffectControl(value, effect, control);
1466           }
1467         }
1468       }
1469     }
1470     Node* storage = receiver;
1471     if (!field_index.is_inobject()) {
1472       storage = effect = graph()->NewNode(
1473           simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
1474           storage, effect, control);
1475     }
1476     FieldAccess field_access = {
1477         kTaggedBase,
1478         field_index.offset(),
1479         name,
1480         MaybeHandle<Map>(),
1481         field_type,
1482         MachineType::TypeForRepresentation(field_representation),
1483         kFullWriteBarrier};
1484     if (access_mode == AccessMode::kLoad) {
1485       if (field_representation == MachineRepresentation::kFloat64) {
1486         if (!field_index.is_inobject() || field_index.is_hidden_field() ||
1487             !FLAG_unbox_double_fields) {
1488           FieldAccess const storage_access = {kTaggedBase,
1489                                               field_index.offset(),
1490                                               name,
1491                                               MaybeHandle<Map>(),
1492                                               Type::OtherInternal(),
1493                                               MachineType::TaggedPointer(),
1494                                               kPointerWriteBarrier};
1495           storage = effect =
1496               graph()->NewNode(simplified()->LoadField(storage_access), storage,
1497                                effect, control);
1498           field_access.offset = HeapNumber::kValueOffset;
1499           field_access.name = MaybeHandle<Name>();
1500         }
1501       } else if (field_representation ==
1502                  MachineRepresentation::kTaggedPointer) {
1503         // Remember the map of the field value, if its map is stable. This is
1504         // used by the LoadElimination to eliminate map checks on the result.
1505         Handle<Map> field_map;
1506         if (access_info.field_map().ToHandle(&field_map)) {
1507           if (field_map->is_stable()) {
1508             dependencies()->AssumeMapStable(field_map);
1509             field_access.map = field_map;
1510           }
1511         }
1512       }
1513       value = effect = graph()->NewNode(simplified()->LoadField(field_access),
1514                                         storage, effect, control);
1515     } else {
1516       bool store_to_constant_field = FLAG_track_constant_fields &&
1517                                      (access_mode == AccessMode::kStore) &&
1518                                      access_info.IsDataConstantField();
1519 
1520       DCHECK(access_mode == AccessMode::kStore ||
1521              access_mode == AccessMode::kStoreInLiteral);
1522       switch (field_representation) {
1523         case MachineRepresentation::kFloat64: {
1524           value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1525                                             effect, control);
1526           if (!field_index.is_inobject() || field_index.is_hidden_field() ||
1527               !FLAG_unbox_double_fields) {
1528             if (access_info.HasTransitionMap()) {
1529               // Allocate a MutableHeapNumber for the new property.
1530               effect = graph()->NewNode(
1531                   common()->BeginRegion(RegionObservability::kNotObservable),
1532                   effect);
1533               Node* box = effect = graph()->NewNode(
1534                   simplified()->Allocate(NOT_TENURED),
1535                   jsgraph()->Constant(HeapNumber::kSize), effect, control);
1536               effect = graph()->NewNode(
1537                   simplified()->StoreField(AccessBuilder::ForMap()), box,
1538                   jsgraph()->HeapConstant(factory()->mutable_heap_number_map()),
1539                   effect, control);
1540               effect = graph()->NewNode(
1541                   simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
1542                   box, value, effect, control);
1543               value = effect =
1544                   graph()->NewNode(common()->FinishRegion(), box, effect);
1545 
1546               field_access.type = Type::Any();
1547               field_access.machine_type = MachineType::TaggedPointer();
1548               field_access.write_barrier_kind = kPointerWriteBarrier;
1549             } else {
1550               // We just store directly to the MutableHeapNumber.
1551               FieldAccess const storage_access = {kTaggedBase,
1552                                                   field_index.offset(),
1553                                                   name,
1554                                                   MaybeHandle<Map>(),
1555                                                   Type::OtherInternal(),
1556                                                   MachineType::TaggedPointer(),
1557                                                   kPointerWriteBarrier};
1558               storage = effect =
1559                   graph()->NewNode(simplified()->LoadField(storage_access),
1560                                    storage, effect, control);
1561               field_access.offset = HeapNumber::kValueOffset;
1562               field_access.name = MaybeHandle<Name>();
1563               field_access.machine_type = MachineType::Float64();
1564             }
1565           }
1566           if (store_to_constant_field) {
1567             DCHECK(!access_info.HasTransitionMap());
1568             // If the field is constant check that the value we are going
1569             // to store matches current value.
1570             Node* current_value = effect =
1571                 graph()->NewNode(simplified()->LoadField(field_access), storage,
1572                                  effect, control);
1573 
1574             Node* check = graph()->NewNode(simplified()->NumberEqual(),
1575                                            current_value, value);
1576             effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
1577                                       control);
1578             return ValueEffectControl(value, effect, control);
1579           }
1580           break;
1581         }
1582         case MachineRepresentation::kTaggedSigned:
1583         case MachineRepresentation::kTaggedPointer:
1584         case MachineRepresentation::kTagged:
1585           if (store_to_constant_field) {
1586             DCHECK(!access_info.HasTransitionMap());
1587             // If the field is constant check that the value we are going
1588             // to store matches current value.
1589             Node* current_value = effect =
1590                 graph()->NewNode(simplified()->LoadField(field_access), storage,
1591                                  effect, control);
1592 
1593             Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1594                                            current_value, value);
1595             effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
1596                                       control);
1597             return ValueEffectControl(value, effect, control);
1598           }
1599 
1600           if (field_representation == MachineRepresentation::kTaggedSigned) {
1601             value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
1602                                               effect, control);
1603             field_access.write_barrier_kind = kNoWriteBarrier;
1604 
1605           } else if (field_representation ==
1606                      MachineRepresentation::kTaggedPointer) {
1607             // Ensure that {value} is a HeapObject.
1608             value = BuildCheckHeapObject(value, &effect, control);
1609             Handle<Map> field_map;
1610             if (access_info.field_map().ToHandle(&field_map)) {
1611               // Emit a map check for the value.
1612               effect = graph()->NewNode(
1613                   simplified()->CheckMaps(CheckMapsFlag::kNone,
1614                                           ZoneHandleSet<Map>(field_map)),
1615                   value, effect, control);
1616             }
1617             field_access.write_barrier_kind = kPointerWriteBarrier;
1618 
1619           } else {
1620             DCHECK_EQ(MachineRepresentation::kTagged, field_representation);
1621           }
1622           break;
1623         case MachineRepresentation::kNone:
1624         case MachineRepresentation::kBit:
1625         case MachineRepresentation::kWord8:
1626         case MachineRepresentation::kWord16:
1627         case MachineRepresentation::kWord32:
1628         case MachineRepresentation::kWord64:
1629         case MachineRepresentation::kFloat32:
1630         case MachineRepresentation::kSimd128:
1631         case MachineRepresentation::kSimd1x4:
1632         case MachineRepresentation::kSimd1x8:
1633         case MachineRepresentation::kSimd1x16:
1634           UNREACHABLE();
1635           break;
1636       }
1637       Handle<Map> transition_map;
1638       if (access_info.transition_map().ToHandle(&transition_map)) {
1639         effect = graph()->NewNode(
1640             common()->BeginRegion(RegionObservability::kObservable), effect);
1641         effect = graph()->NewNode(
1642             simplified()->StoreField(AccessBuilder::ForMap()), receiver,
1643             jsgraph()->Constant(transition_map), effect, control);
1644       }
1645       effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
1646                                 value, effect, control);
1647       if (access_info.HasTransitionMap()) {
1648         effect = graph()->NewNode(common()->FinishRegion(),
1649                                   jsgraph()->UndefinedConstant(), effect);
1650       }
1651     }
1652   } else {
1653     DCHECK(access_info.IsGeneric());
1654     DCHECK_EQ(AccessMode::kStore, access_mode);
1655     DCHECK(vector->IsStoreIC(slot));
1656     DCHECK_EQ(vector->GetLanguageMode(slot), language_mode);
1657     Callable callable =
1658         CodeFactory::StoreICInOptimizedCode(isolate(), language_mode);
1659     const CallInterfaceDescriptor& descriptor = callable.descriptor();
1660     CallDescriptor* desc = Linkage::GetStubCallDescriptor(
1661         isolate(), graph()->zone(), descriptor,
1662         descriptor.GetStackParameterCount(), CallDescriptor::kNeedsFrameState,
1663         Operator::kNoProperties);
1664     Node* stub_code = jsgraph()->HeapConstant(callable.code());
1665     Node* name_node = jsgraph()->HeapConstant(name);
1666     Node* slot_node = jsgraph()->Constant(vector->GetIndex(slot));
1667     Node* vector_node = jsgraph()->HeapConstant(vector);
1668 
1669     Node* inputs[] = {stub_code,   receiver, name_node,   value,  slot_node,
1670                       vector_node, context,  frame_state, effect, control};
1671 
1672     value = effect = control =
1673         graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
1674     control = graph()->NewNode(common()->IfSuccess(), control);
1675   }
1676 
1677   return ValueEffectControl(value, effect, control);
1678 }
1679 
ReduceJSStoreDataPropertyInLiteral(Node * node)1680 Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
1681     Node* node) {
1682   DCHECK_EQ(IrOpcode::kJSStoreDataPropertyInLiteral, node->opcode());
1683 
1684   // If deoptimization is disabled, we cannot optimize.
1685   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
1686 
1687   DataPropertyParameters const& p = DataPropertyParametersOf(node->op());
1688 
1689   if (!p.feedback().IsValid()) return NoChange();
1690 
1691   StoreDataPropertyInLiteralICNexus nexus(p.feedback().vector(),
1692                                           p.feedback().slot());
1693   if (nexus.IsUninitialized()) {
1694     return NoChange();
1695   }
1696 
1697   if (nexus.ic_state() == MEGAMORPHIC) {
1698     return NoChange();
1699   }
1700 
1701   DCHECK_EQ(MONOMORPHIC, nexus.ic_state());
1702 
1703   Map* map = nexus.FindFirstMap();
1704   if (map == nullptr) {
1705     // Maps are weakly held in the type feedback vector, we may not have one.
1706     return NoChange();
1707   }
1708 
1709   Handle<Map> receiver_map(map, isolate());
1710   Handle<Name> cached_name =
1711       handle(Name::cast(nexus.GetFeedbackExtra()), isolate());
1712 
1713   PropertyAccessInfo access_info;
1714   AccessInfoFactory access_info_factory(dependencies(), native_context(),
1715                                         graph()->zone());
1716   if (!access_info_factory.ComputePropertyAccessInfo(
1717           receiver_map, cached_name, AccessMode::kStoreInLiteral,
1718           &access_info)) {
1719     return NoChange();
1720   }
1721 
1722   if (access_info.IsGeneric()) {
1723     return NoChange();
1724   }
1725 
1726   Node* receiver = NodeProperties::GetValueInput(node, 0);
1727   Node* effect = NodeProperties::GetEffectInput(node);
1728   Node* control = NodeProperties::GetControlInput(node);
1729 
1730   // Monomorphic property access.
1731   receiver = BuildCheckHeapObject(receiver, &effect, control);
1732 
1733   effect =
1734       BuildCheckMaps(receiver, effect, control, access_info.receiver_maps());
1735 
1736   // Ensure that {name} matches the cached name.
1737   Node* name = NodeProperties::GetValueInput(node, 1);
1738   Node* check = graph()->NewNode(simplified()->ReferenceEqual(), name,
1739                                  jsgraph()->HeapConstant(cached_name));
1740   effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
1741 
1742   Node* value = NodeProperties::GetValueInput(node, 2);
1743   Node* context = NodeProperties::GetContextInput(node);
1744   Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node);
1745 
1746   // Generate the actual property access.
1747   ValueEffectControl continuation = BuildPropertyAccess(
1748       receiver, value, context, frame_state_lazy, effect, control, cached_name,
1749       access_info, AccessMode::kStoreInLiteral, LanguageMode::SLOPPY,
1750       p.feedback().vector(), p.feedback().slot());
1751   value = continuation.value();
1752   effect = continuation.effect();
1753   control = continuation.control();
1754 
1755   ReplaceWithValue(node, value, effect, control);
1756   return Replace(value);
1757 }
1758 
1759 namespace {
1760 
GetArrayTypeFromElementsKind(ElementsKind kind)1761 ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
1762   switch (kind) {
1763 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
1764   case TYPE##_ELEMENTS:                                 \
1765     return kExternal##Type##Array;
1766     TYPED_ARRAYS(TYPED_ARRAY_CASE)
1767 #undef TYPED_ARRAY_CASE
1768     default:
1769       break;
1770   }
1771   UNREACHABLE();
1772   return kExternalInt8Array;
1773 }
1774 
1775 }  // namespace
1776 
1777 JSNativeContextSpecialization::ValueEffectControl
BuildElementAccess(Node * receiver,Node * index,Node * value,Node * effect,Node * control,ElementAccessInfo const & access_info,AccessMode access_mode,KeyedAccessStoreMode store_mode)1778 JSNativeContextSpecialization::BuildElementAccess(
1779     Node* receiver, Node* index, Node* value, Node* effect, Node* control,
1780     ElementAccessInfo const& access_info, AccessMode access_mode,
1781     KeyedAccessStoreMode store_mode) {
1782   DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
1783 
1784   // TODO(bmeurer): We currently specialize based on elements kind. We should
1785   // also be able to properly support strings and other JSObjects here.
1786   ElementsKind elements_kind = access_info.elements_kind();
1787   MapList const& receiver_maps = access_info.receiver_maps();
1788 
1789   if (IsFixedTypedArrayElementsKind(elements_kind)) {
1790     Node* buffer;
1791     Node* length;
1792     Node* base_pointer;
1793     Node* external_pointer;
1794 
1795     // Check if we can constant-fold information about the {receiver} (i.e.
1796     // for asm.js-like code patterns).
1797     HeapObjectMatcher m(receiver);
1798     if (m.HasValue() && m.Value()->IsJSTypedArray()) {
1799       Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(m.Value());
1800 
1801       // Determine the {receiver}s (known) length.
1802       length = jsgraph()->Constant(typed_array->length_value());
1803 
1804       // Check if the {receiver}s buffer was neutered.
1805       buffer = jsgraph()->HeapConstant(typed_array->GetBuffer());
1806 
1807       // Load the (known) base and external pointer for the {receiver}. The
1808       // {external_pointer} might be invalid if the {buffer} was neutered, so
1809       // we need to make sure that any access is properly guarded.
1810       base_pointer = jsgraph()->ZeroConstant();
1811       external_pointer = jsgraph()->PointerConstant(
1812           FixedTypedArrayBase::cast(typed_array->elements())
1813               ->external_pointer());
1814     } else {
1815       // Load the {receiver}s length.
1816       length = effect = graph()->NewNode(
1817           simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
1818           receiver, effect, control);
1819 
1820       // Load the buffer for the {receiver}.
1821       buffer = effect = graph()->NewNode(
1822           simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
1823           receiver, effect, control);
1824 
1825       // Load the elements for the {receiver}.
1826       Node* elements = effect = graph()->NewNode(
1827           simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
1828           receiver, effect, control);
1829 
1830       // Load the base and external pointer for the {receiver}s {elements}.
1831       base_pointer = effect = graph()->NewNode(
1832           simplified()->LoadField(
1833               AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
1834           elements, effect, control);
1835       external_pointer = effect = graph()->NewNode(
1836           simplified()->LoadField(
1837               AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
1838           elements, effect, control);
1839     }
1840 
1841     // See if we can skip the neutering check.
1842     if (isolate()->IsArrayBufferNeuteringIntact()) {
1843       // Add a code dependency so we are deoptimized in case an ArrayBuffer
1844       // gets neutered.
1845       dependencies()->AssumePropertyCell(
1846           factory()->array_buffer_neutering_protector());
1847     } else {
1848       // Default to zero if the {receiver}s buffer was neutered.
1849       Node* check = effect = graph()->NewNode(
1850           simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
1851       length = graph()->NewNode(
1852           common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
1853           check, jsgraph()->ZeroConstant(), length);
1854     }
1855 
1856     if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
1857       // Check that the {index} is a valid array index, we do the actual
1858       // bounds check below and just skip the store below if it's out of
1859       // bounds for the {receiver}.
1860       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1861                                         jsgraph()->Constant(Smi::kMaxValue),
1862                                         effect, control);
1863     } else {
1864       // Check that the {index} is in the valid range for the {receiver}.
1865       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1866                                         length, effect, control);
1867     }
1868 
1869     // Access the actual element.
1870     ExternalArrayType external_array_type =
1871         GetArrayTypeFromElementsKind(elements_kind);
1872     switch (access_mode) {
1873       case AccessMode::kLoad: {
1874         value = effect = graph()->NewNode(
1875             simplified()->LoadTypedElement(external_array_type), buffer,
1876             base_pointer, external_pointer, index, effect, control);
1877         break;
1878       }
1879       case AccessMode::kStoreInLiteral:
1880         UNREACHABLE();
1881         break;
1882       case AccessMode::kStore: {
1883         // Ensure that the {value} is actually a Number.
1884         value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1885                                           effect, control);
1886 
1887         // Introduce the appropriate truncation for {value}. Currently we
1888         // only need to do this for ClamedUint8Array {receiver}s, as the
1889         // other truncations are implicit in the StoreTypedElement, but we
1890         // might want to change that at some point.
1891         if (external_array_type == kExternalUint8ClampedArray) {
1892           value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
1893         }
1894 
1895         // Check if we can skip the out-of-bounds store.
1896         if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
1897           Node* check =
1898               graph()->NewNode(simplified()->NumberLessThan(), index, length);
1899           Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1900                                           check, control);
1901 
1902           Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1903           Node* etrue = effect;
1904           {
1905             // Perform the actual store.
1906             etrue = graph()->NewNode(
1907                 simplified()->StoreTypedElement(external_array_type), buffer,
1908                 base_pointer, external_pointer, index, value, etrue, if_true);
1909           }
1910 
1911           Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1912           Node* efalse = effect;
1913           {
1914             // Just ignore the out-of-bounds write.
1915           }
1916 
1917           control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1918           effect =
1919               graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1920         } else {
1921           // Perform the actual store
1922           effect = graph()->NewNode(
1923               simplified()->StoreTypedElement(external_array_type), buffer,
1924               base_pointer, external_pointer, index, value, effect, control);
1925         }
1926         break;
1927       }
1928     }
1929   } else {
1930     // Load the elements for the {receiver}.
1931     Node* elements = effect = graph()->NewNode(
1932         simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
1933         effect, control);
1934 
1935     // Don't try to store to a copy-on-write backing store.
1936     if (access_mode == AccessMode::kStore &&
1937         IsFastSmiOrObjectElementsKind(elements_kind) &&
1938         store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
1939       effect = graph()->NewNode(
1940           simplified()->CheckMaps(
1941               CheckMapsFlag::kNone,
1942               ZoneHandleSet<Map>(factory()->fixed_array_map())),
1943           elements, effect, control);
1944     }
1945 
1946     // Check if the {receiver} is a JSArray.
1947     bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps);
1948 
1949     // Load the length of the {receiver}.
1950     Node* length = effect =
1951         receiver_is_jsarray
1952             ? graph()->NewNode(
1953                   simplified()->LoadField(
1954                       AccessBuilder::ForJSArrayLength(elements_kind)),
1955                   receiver, effect, control)
1956             : graph()->NewNode(
1957                   simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
1958                   elements, effect, control);
1959 
1960     // Check if we might need to grow the {elements} backing store.
1961     if (IsGrowStoreMode(store_mode)) {
1962       DCHECK_EQ(AccessMode::kStore, access_mode);
1963 
1964       // Check that the {index} is a valid array index; the actual checking
1965       // happens below right before the element store.
1966       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1967                                         jsgraph()->Constant(Smi::kMaxValue),
1968                                         effect, control);
1969     } else {
1970       // Check that the {index} is in the valid range for the {receiver}.
1971       index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1972                                         length, effect, control);
1973     }
1974 
1975     // Compute the element access.
1976     Type* element_type = Type::NonInternal();
1977     MachineType element_machine_type = MachineType::AnyTagged();
1978     if (IsFastDoubleElementsKind(elements_kind)) {
1979       element_type = Type::Number();
1980       element_machine_type = MachineType::Float64();
1981     } else if (IsFastSmiElementsKind(elements_kind)) {
1982       element_type = Type::SignedSmall();
1983       element_machine_type = MachineType::TaggedSigned();
1984     }
1985     ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
1986                                     element_type, element_machine_type,
1987                                     kFullWriteBarrier};
1988 
1989     // Access the actual element.
1990     if (access_mode == AccessMode::kLoad) {
1991       // Compute the real element access type, which includes the hole in case
1992       // of holey backing stores.
1993       if (elements_kind == FAST_HOLEY_ELEMENTS ||
1994           elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
1995         element_access.type =
1996             Type::Union(element_type, Type::Hole(), graph()->zone());
1997         element_access.machine_type = MachineType::AnyTagged();
1998       }
1999       // Perform the actual backing store access.
2000       value = effect =
2001           graph()->NewNode(simplified()->LoadElement(element_access), elements,
2002                            index, effect, control);
2003       // Handle loading from holey backing stores correctly, by either mapping
2004       // the hole to undefined if possible, or deoptimizing otherwise.
2005       if (elements_kind == FAST_HOLEY_ELEMENTS ||
2006           elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
2007         // Check if we are allowed to turn the hole into undefined.
2008         if (CanTreatHoleAsUndefined(receiver_maps)) {
2009           // Turn the hole into undefined.
2010           value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
2011                                    value);
2012         } else {
2013           // Bailout if we see the hole.
2014           value = effect = graph()->NewNode(simplified()->CheckTaggedHole(),
2015                                             value, effect, control);
2016         }
2017       } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
2018         // Perform the hole check on the result.
2019         CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
2020         // Check if we are allowed to return the hole directly.
2021         if (CanTreatHoleAsUndefined(receiver_maps)) {
2022           // Return the signaling NaN hole directly if all uses are truncating.
2023           mode = CheckFloat64HoleMode::kAllowReturnHole;
2024         }
2025         value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
2026                                           value, effect, control);
2027       }
2028     } else {
2029       DCHECK_EQ(AccessMode::kStore, access_mode);
2030       if (IsFastSmiElementsKind(elements_kind)) {
2031         value = effect =
2032             graph()->NewNode(simplified()->CheckSmi(), value, effect, control);
2033       } else if (IsFastDoubleElementsKind(elements_kind)) {
2034         value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
2035                                           effect, control);
2036         // Make sure we do not store signalling NaNs into double arrays.
2037         value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
2038       }
2039 
2040       // Ensure that copy-on-write backing store is writable.
2041       if (IsFastSmiOrObjectElementsKind(elements_kind) &&
2042           store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
2043         elements = effect =
2044             graph()->NewNode(simplified()->EnsureWritableFastElements(),
2045                              receiver, elements, effect, control);
2046       } else if (IsGrowStoreMode(store_mode)) {
2047         // Grow {elements} backing store if necessary. Also updates the
2048         // "length" property for JSArray {receiver}s, hence there must
2049         // not be any other check after this operation, as the write
2050         // to the "length" property is observable.
2051         GrowFastElementsFlags flags = GrowFastElementsFlag::kNone;
2052         if (receiver_is_jsarray) {
2053           flags |= GrowFastElementsFlag::kArrayObject;
2054         }
2055         if (IsHoleyElementsKind(elements_kind)) {
2056           flags |= GrowFastElementsFlag::kHoleyElements;
2057         }
2058         if (IsFastDoubleElementsKind(elements_kind)) {
2059           flags |= GrowFastElementsFlag::kDoubleElements;
2060         }
2061         elements = effect = graph()->NewNode(
2062             simplified()->MaybeGrowFastElements(flags), receiver, elements,
2063             index, length, effect, control);
2064       }
2065 
2066       // Perform the actual element access.
2067       effect = graph()->NewNode(simplified()->StoreElement(element_access),
2068                                 elements, index, value, effect, control);
2069     }
2070   }
2071 
2072   return ValueEffectControl(value, effect, control);
2073 }
2074 
2075 JSNativeContextSpecialization::ValueEffectControl
InlineApiCall(Node * receiver,Node * context,Node * target,Node * frame_state,Node * value,Node * effect,Node * control,Handle<SharedFunctionInfo> shared_info,Handle<FunctionTemplateInfo> function_template_info)2076 JSNativeContextSpecialization::InlineApiCall(
2077     Node* receiver, Node* context, Node* target, Node* frame_state, Node* value,
2078     Node* effect, Node* control, Handle<SharedFunctionInfo> shared_info,
2079     Handle<FunctionTemplateInfo> function_template_info) {
2080   Handle<CallHandlerInfo> call_handler_info = handle(
2081       CallHandlerInfo::cast(function_template_info->call_code()), isolate());
2082   Handle<Object> call_data_object(call_handler_info->data(), isolate());
2083 
2084   // Only setters have a value.
2085   int const argc = value == nullptr ? 0 : 1;
2086   // The stub always expects the receiver as the first param on the stack.
2087   CallApiCallbackStub stub(
2088       isolate(), argc, call_data_object->IsUndefined(isolate()),
2089       true /* FunctionTemplateInfo doesn't have an associated context. */);
2090   CallInterfaceDescriptor call_interface_descriptor =
2091       stub.GetCallInterfaceDescriptor();
2092   CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
2093       isolate(), graph()->zone(), call_interface_descriptor,
2094       call_interface_descriptor.GetStackParameterCount() + argc +
2095           1 /* implicit receiver */,
2096       CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
2097       MachineType::AnyTagged(), 1);
2098 
2099   Node* data = jsgraph()->Constant(call_data_object);
2100   ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
2101   Node* function_reference =
2102       graph()->NewNode(common()->ExternalConstant(ExternalReference(
2103           &function, ExternalReference::DIRECT_API_CALL, isolate())));
2104   Node* code = jsgraph()->HeapConstant(stub.GetCode());
2105 
2106   // Add CallApiCallbackStub's register argument as well.
2107   Node* inputs[11] = {
2108       code, target, data, receiver /* holder */, function_reference, receiver};
2109   int index = 6 + argc;
2110   inputs[index++] = context;
2111   inputs[index++] = frame_state;
2112   inputs[index++] = effect;
2113   inputs[index++] = control;
2114   // This needs to stay here because of the edge case described in
2115   // http://crbug.com/675648.
2116   if (value != nullptr) {
2117     inputs[6] = value;
2118   }
2119 
2120   Node* effect0;
2121   Node* value0 = effect0 =
2122       graph()->NewNode(common()->Call(call_descriptor), index, inputs);
2123   Node* control0 = graph()->NewNode(common()->IfSuccess(), value0);
2124   return ValueEffectControl(value0, effect0, control0);
2125 }
2126 
BuildCheckHeapObject(Node * receiver,Node ** effect,Node * control)2127 Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver,
2128                                                           Node** effect,
2129                                                           Node* control) {
2130   switch (receiver->opcode()) {
2131     case IrOpcode::kHeapConstant:
2132     case IrOpcode::kJSCreate:
2133     case IrOpcode::kJSCreateArguments:
2134     case IrOpcode::kJSCreateArray:
2135     case IrOpcode::kJSCreateClosure:
2136     case IrOpcode::kJSCreateIterResultObject:
2137     case IrOpcode::kJSCreateLiteralArray:
2138     case IrOpcode::kJSCreateLiteralObject:
2139     case IrOpcode::kJSCreateLiteralRegExp:
2140     case IrOpcode::kJSConvertReceiver:
2141     case IrOpcode::kJSToName:
2142     case IrOpcode::kJSToString:
2143     case IrOpcode::kJSToObject:
2144     case IrOpcode::kJSTypeOf: {
2145       return receiver;
2146     }
2147     default: {
2148       return *effect = graph()->NewNode(simplified()->CheckHeapObject(),
2149                                         receiver, *effect, control);
2150     }
2151   }
2152 }
2153 
BuildCheckMaps(Node * receiver,Node * effect,Node * control,std::vector<Handle<Map>> const & receiver_maps)2154 Node* JSNativeContextSpecialization::BuildCheckMaps(
2155     Node* receiver, Node* effect, Node* control,
2156     std::vector<Handle<Map>> const& receiver_maps) {
2157   HeapObjectMatcher m(receiver);
2158   if (m.HasValue()) {
2159     Handle<Map> receiver_map(m.Value()->map(), isolate());
2160     if (receiver_map->is_stable()) {
2161       for (Handle<Map> map : receiver_maps) {
2162         if (map.is_identical_to(receiver_map)) {
2163           dependencies()->AssumeMapStable(receiver_map);
2164           return effect;
2165         }
2166       }
2167     }
2168   }
2169   ZoneHandleSet<Map> maps;
2170   CheckMapsFlags flags = CheckMapsFlag::kNone;
2171   for (Handle<Map> map : receiver_maps) {
2172     maps.insert(map, graph()->zone());
2173     if (map->is_migration_target()) {
2174       flags |= CheckMapsFlag::kTryMigrateInstance;
2175     }
2176   }
2177   return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
2178                           effect, control);
2179 }
2180 
AssumePrototypesStable(std::vector<Handle<Map>> const & receiver_maps,Handle<JSObject> holder)2181 void JSNativeContextSpecialization::AssumePrototypesStable(
2182     std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
2183   // Determine actual holder and perform prototype chain checks.
2184   for (auto map : receiver_maps) {
2185     // Perform the implicit ToObject for primitives here.
2186     // Implemented according to ES6 section 7.3.2 GetV (V, P).
2187     Handle<JSFunction> constructor;
2188     if (Map::GetConstructorFunction(map, native_context())
2189             .ToHandle(&constructor)) {
2190       map = handle(constructor->initial_map(), isolate());
2191     }
2192     dependencies()->AssumePrototypeMapsStable(map, holder);
2193   }
2194 }
2195 
CanTreatHoleAsUndefined(std::vector<Handle<Map>> const & receiver_maps)2196 bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
2197     std::vector<Handle<Map>> const& receiver_maps) {
2198   // Check if the array prototype chain is intact.
2199   if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false;
2200 
2201   // Make sure both the initial Array and Object prototypes are stable.
2202   Handle<JSObject> initial_array_prototype(
2203       native_context()->initial_array_prototype(), isolate());
2204   Handle<JSObject> initial_object_prototype(
2205       native_context()->initial_object_prototype(), isolate());
2206   if (!initial_array_prototype->map()->is_stable() ||
2207       !initial_object_prototype->map()->is_stable()) {
2208     return false;
2209   }
2210 
2211   // Check if all {receiver_maps} either have the initial Array.prototype
2212   // or the initial Object.prototype as their prototype, as those are
2213   // guarded by the array protector cell.
2214   for (Handle<Map> map : receiver_maps) {
2215     if (map->prototype() != *initial_array_prototype &&
2216         map->prototype() != *initial_object_prototype) {
2217       return false;
2218     }
2219   }
2220 
2221   // Install code dependencies on the prototype maps.
2222   for (Handle<Map> map : receiver_maps) {
2223     dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype);
2224   }
2225 
2226   // Install code dependency on the array protector cell.
2227   dependencies()->AssumePropertyCell(factory()->array_protector());
2228   return true;
2229 }
2230 
ExtractReceiverMaps(Node * receiver,Node * effect,FeedbackNexus const & nexus,MapHandleList * receiver_maps)2231 bool JSNativeContextSpecialization::ExtractReceiverMaps(
2232     Node* receiver, Node* effect, FeedbackNexus const& nexus,
2233     MapHandleList* receiver_maps) {
2234   DCHECK_EQ(0, receiver_maps->length());
2235   // See if we can infer a concrete type for the {receiver}.
2236   if (InferReceiverMaps(receiver, effect, receiver_maps)) {
2237     // We can assume that the {receiver} still has the infered {receiver_maps}.
2238     return true;
2239   }
2240   // Try to extract some maps from the {nexus}.
2241   if (nexus.ExtractMaps(receiver_maps) != 0) {
2242     // Try to filter impossible candidates based on infered root map.
2243     Handle<Map> receiver_map;
2244     if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) {
2245       for (int i = receiver_maps->length(); --i >= 0;) {
2246         if (receiver_maps->at(i)->FindRootMap() != *receiver_map) {
2247           receiver_maps->Remove(i);
2248         }
2249       }
2250     }
2251     return true;
2252   }
2253   return false;
2254 }
2255 
InferReceiverMaps(Node * receiver,Node * effect,MapHandleList * receiver_maps)2256 bool JSNativeContextSpecialization::InferReceiverMaps(
2257     Node* receiver, Node* effect, MapHandleList* receiver_maps) {
2258   ZoneHandleSet<Map> maps;
2259   NodeProperties::InferReceiverMapsResult result =
2260       NodeProperties::InferReceiverMaps(receiver, effect, &maps);
2261   if (result == NodeProperties::kReliableReceiverMaps) {
2262     for (size_t i = 0; i < maps.size(); ++i) {
2263       receiver_maps->Add(maps[i]);
2264     }
2265     return true;
2266   } else if (result == NodeProperties::kUnreliableReceiverMaps) {
2267     // For untrusted receiver maps, we can still use the information
2268     // if the maps are stable.
2269     for (size_t i = 0; i < maps.size(); ++i) {
2270       if (!maps[i]->is_stable()) return false;
2271     }
2272     for (size_t i = 0; i < maps.size(); ++i) {
2273       receiver_maps->Add(maps[i]);
2274     }
2275     return true;
2276   }
2277   return false;
2278 }
2279 
InferReceiverRootMap(Node * receiver)2280 MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
2281     Node* receiver) {
2282   HeapObjectMatcher m(receiver);
2283   if (m.HasValue()) {
2284     return handle(m.Value()->map()->FindRootMap(), isolate());
2285   } else if (m.IsJSCreate()) {
2286     HeapObjectMatcher mtarget(m.InputAt(0));
2287     HeapObjectMatcher mnewtarget(m.InputAt(1));
2288     if (mtarget.HasValue() && mnewtarget.HasValue()) {
2289       Handle<JSFunction> constructor =
2290           Handle<JSFunction>::cast(mtarget.Value());
2291       if (constructor->has_initial_map()) {
2292         Handle<Map> initial_map(constructor->initial_map(), isolate());
2293         if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) {
2294           DCHECK_EQ(*initial_map, initial_map->FindRootMap());
2295           return initial_map;
2296         }
2297       }
2298     }
2299   }
2300   return MaybeHandle<Map>();
2301 }
2302 
LookupInScriptContextTable(Handle<Name> name,ScriptContextTableLookupResult * result)2303 bool JSNativeContextSpecialization::LookupInScriptContextTable(
2304     Handle<Name> name, ScriptContextTableLookupResult* result) {
2305   if (!name->IsString()) return false;
2306   Handle<ScriptContextTable> script_context_table(
2307       global_object()->native_context()->script_context_table(), isolate());
2308   ScriptContextTable::LookupResult lookup_result;
2309   if (!ScriptContextTable::Lookup(script_context_table,
2310                                   Handle<String>::cast(name), &lookup_result)) {
2311     return false;
2312   }
2313   Handle<Context> script_context = ScriptContextTable::GetContext(
2314       script_context_table, lookup_result.context_index);
2315   result->context = script_context;
2316   result->immutable = lookup_result.mode == CONST;
2317   result->index = lookup_result.slot_index;
2318   return true;
2319 }
2320 
graph() const2321 Graph* JSNativeContextSpecialization::graph() const {
2322   return jsgraph()->graph();
2323 }
2324 
isolate() const2325 Isolate* JSNativeContextSpecialization::isolate() const {
2326   return jsgraph()->isolate();
2327 }
2328 
factory() const2329 Factory* JSNativeContextSpecialization::factory() const {
2330   return isolate()->factory();
2331 }
2332 
machine() const2333 MachineOperatorBuilder* JSNativeContextSpecialization::machine() const {
2334   return jsgraph()->machine();
2335 }
2336 
common() const2337 CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
2338   return jsgraph()->common();
2339 }
2340 
javascript() const2341 JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
2342   return jsgraph()->javascript();
2343 }
2344 
simplified() const2345 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
2346   return jsgraph()->simplified();
2347 }
2348 
2349 }  // namespace compiler
2350 }  // namespace internal
2351 }  // namespace v8
2352