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