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