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/field-index-inl.h"
17 #include "src/isolate-inl.h"
18 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
19 #include "src/type-cache.h"
20 #include "src/type-feedback-vector.h"
21
22 namespace v8 {
23 namespace internal {
24 namespace compiler {
25
JSNativeContextSpecialization(Editor * editor,JSGraph * jsgraph,Flags flags,MaybeHandle<Context> native_context,CompilationDependencies * dependencies,Zone * zone)26 JSNativeContextSpecialization::JSNativeContextSpecialization(
27 Editor* editor, JSGraph* jsgraph, Flags flags,
28 MaybeHandle<Context> native_context, CompilationDependencies* dependencies,
29 Zone* zone)
30 : AdvancedReducer(editor),
31 jsgraph_(jsgraph),
32 flags_(flags),
33 native_context_(native_context),
34 dependencies_(dependencies),
35 zone_(zone),
36 type_cache_(TypeCache::Get()) {}
37
38
Reduce(Node * node)39 Reduction JSNativeContextSpecialization::Reduce(Node* node) {
40 switch (node->opcode()) {
41 case IrOpcode::kJSLoadNamed:
42 return ReduceJSLoadNamed(node);
43 case IrOpcode::kJSStoreNamed:
44 return ReduceJSStoreNamed(node);
45 case IrOpcode::kJSLoadProperty:
46 return ReduceJSLoadProperty(node);
47 case IrOpcode::kJSStoreProperty:
48 return ReduceJSStoreProperty(node);
49 default:
50 break;
51 }
52 return NoChange();
53 }
54
55
ReduceNamedAccess(Node * node,Node * value,MapHandleList const & receiver_maps,Handle<Name> name,AccessMode access_mode,LanguageMode language_mode,Node * index)56 Reduction JSNativeContextSpecialization::ReduceNamedAccess(
57 Node* node, Node* value, MapHandleList const& receiver_maps,
58 Handle<Name> name, AccessMode access_mode, LanguageMode language_mode,
59 Node* index) {
60 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
61 node->opcode() == IrOpcode::kJSStoreNamed ||
62 node->opcode() == IrOpcode::kJSLoadProperty ||
63 node->opcode() == IrOpcode::kJSStoreProperty);
64 Node* receiver = NodeProperties::GetValueInput(node, 0);
65 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
66 Node* effect = NodeProperties::GetEffectInput(node);
67 Node* control = NodeProperties::GetControlInput(node);
68
69 // Not much we can do if deoptimization support is disabled.
70 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
71
72 // Retrieve the native context from the given {node}.
73 Handle<Context> native_context;
74 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange();
75
76 // Compute property access infos for the receiver maps.
77 AccessInfoFactory access_info_factory(dependencies(), native_context,
78 graph()->zone());
79 ZoneVector<PropertyAccessInfo> access_infos(zone());
80 if (!access_info_factory.ComputePropertyAccessInfos(
81 receiver_maps, name, access_mode, &access_infos)) {
82 return NoChange();
83 }
84
85 // Nothing to do if we have no non-deprecated maps.
86 if (access_infos.empty()) return NoChange();
87
88 // The final states for every polymorphic branch. We join them with
89 // Merge++Phi+EffectPhi at the bottom.
90 ZoneVector<Node*> values(zone());
91 ZoneVector<Node*> effects(zone());
92 ZoneVector<Node*> controls(zone());
93
94 // The list of "exiting" controls, which currently go to a single deoptimize.
95 // TODO(bmeurer): Consider using an IC as fallback.
96 Node* const exit_effect = effect;
97 ZoneVector<Node*> exit_controls(zone());
98
99 // Ensure that {index} matches the specified {name} (if {index} is given).
100 if (index != nullptr) {
101 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()),
102 index, jsgraph()->HeapConstant(name));
103 Node* branch =
104 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
105 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
106 control = graph()->NewNode(common()->IfTrue(), branch);
107 }
108
109 // Ensure that {receiver} is a heap object.
110 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
111 Node* branch = graph()->NewNode(common()->Branch(), check, control);
112 control = graph()->NewNode(common()->IfFalse(), branch);
113 Node* receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
114 Node* receiverissmi_effect = effect;
115
116 // Load the {receiver} map. The resulting effect is the dominating effect for
117 // all (polymorphic) branches.
118 Node* receiver_map = effect =
119 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
120 receiver, effect, control);
121
122 // Generate code for the various different property access patterns.
123 Node* fallthrough_control = control;
124 for (PropertyAccessInfo const& access_info : access_infos) {
125 Node* this_value = value;
126 Node* this_receiver = receiver;
127 Node* this_effect = effect;
128 Node* this_control;
129
130 // Perform map check on {receiver}.
131 Type* receiver_type = access_info.receiver_type();
132 if (receiver_type->Is(Type::String())) {
133 // Emit an instance type check for strings.
134 Node* receiver_instance_type = this_effect = graph()->NewNode(
135 simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
136 receiver_map, this_effect, fallthrough_control);
137 Node* check =
138 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type,
139 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE));
140 Node* branch =
141 graph()->NewNode(common()->Branch(), check, fallthrough_control);
142 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
143 this_control = graph()->NewNode(common()->IfTrue(), branch);
144 } else {
145 // Emit a (sequence of) map checks for other {receiver}s.
146 ZoneVector<Node*> this_controls(zone());
147 ZoneVector<Node*> this_effects(zone());
148 for (auto i = access_info.receiver_type()->Classes(); !i.Done();
149 i.Advance()) {
150 Handle<Map> map = i.Current();
151 Node* check =
152 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
153 receiver_map, jsgraph()->Constant(map));
154 Node* branch =
155 graph()->NewNode(common()->Branch(), check, fallthrough_control);
156 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
157 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
158 this_effects.push_back(this_effect);
159 }
160
161 // The Number case requires special treatment to also deal with Smis.
162 if (receiver_type->Is(Type::Number())) {
163 // Join this check with the "receiver is smi" check above, and mark the
164 // "receiver is smi" check as "consumed" so that we don't deoptimize if
165 // the {receiver} is actually a Smi.
166 if (receiverissmi_control != nullptr) {
167 this_controls.push_back(receiverissmi_control);
168 this_effects.push_back(receiverissmi_effect);
169 receiverissmi_control = receiverissmi_effect = nullptr;
170 }
171 }
172
173 // Create dominating Merge+EffectPhi for this {receiver} type.
174 int const this_control_count = static_cast<int>(this_controls.size());
175 this_control =
176 (this_control_count == 1)
177 ? this_controls.front()
178 : graph()->NewNode(common()->Merge(this_control_count),
179 this_control_count, &this_controls.front());
180 this_effects.push_back(this_control);
181 int const this_effect_count = static_cast<int>(this_effects.size());
182 this_effect =
183 (this_control_count == 1)
184 ? this_effects.front()
185 : graph()->NewNode(common()->EffectPhi(this_control_count),
186 this_effect_count, &this_effects.front());
187 }
188
189 // Determine actual holder and perform prototype chain checks.
190 Handle<JSObject> holder;
191 if (access_info.holder().ToHandle(&holder)) {
192 AssumePrototypesStable(receiver_type, native_context, holder);
193 }
194
195 // Generate the actual property access.
196 if (access_info.IsNotFound()) {
197 DCHECK_EQ(AccessMode::kLoad, access_mode);
198 if (is_strong(language_mode)) {
199 // TODO(bmeurer/mstarzinger): Add support for lowering inside try
200 // blocks rewiring the IfException edge to a runtime call/throw.
201 exit_controls.push_back(this_control);
202 continue;
203 } else {
204 this_value = jsgraph()->UndefinedConstant();
205 }
206 } else if (access_info.IsDataConstant()) {
207 this_value = jsgraph()->Constant(access_info.constant());
208 if (access_mode == AccessMode::kStore) {
209 Node* check = graph()->NewNode(
210 simplified()->ReferenceEqual(Type::Tagged()), value, this_value);
211 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
212 check, this_control);
213 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
214 this_control = graph()->NewNode(common()->IfTrue(), branch);
215 }
216 } else {
217 DCHECK(access_info.IsDataField());
218 FieldIndex const field_index = access_info.field_index();
219 FieldCheck const field_check = access_info.field_check();
220 Type* const field_type = access_info.field_type();
221 switch (field_check) {
222 case FieldCheck::kNone:
223 break;
224 case FieldCheck::kJSArrayBufferViewBufferNotNeutered: {
225 Node* this_buffer = this_effect =
226 graph()->NewNode(simplified()->LoadField(
227 AccessBuilder::ForJSArrayBufferViewBuffer()),
228 this_receiver, this_effect, this_control);
229 Node* this_buffer_bit_field = this_effect =
230 graph()->NewNode(simplified()->LoadField(
231 AccessBuilder::ForJSArrayBufferBitField()),
232 this_buffer, this_effect, this_control);
233 Node* check = graph()->NewNode(
234 machine()->Word32Equal(),
235 graph()->NewNode(machine()->Word32And(), this_buffer_bit_field,
236 jsgraph()->Int32Constant(
237 1 << JSArrayBuffer::WasNeutered::kShift)),
238 jsgraph()->Int32Constant(0));
239 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
240 check, this_control);
241 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
242 this_control = graph()->NewNode(common()->IfFalse(), branch);
243 break;
244 }
245 }
246 if (access_mode == AccessMode::kLoad &&
247 access_info.holder().ToHandle(&holder)) {
248 this_receiver = jsgraph()->Constant(holder);
249 }
250 Node* this_storage = this_receiver;
251 if (!field_index.is_inobject()) {
252 this_storage = this_effect = graph()->NewNode(
253 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
254 this_storage, this_effect, this_control);
255 }
256 FieldAccess field_access = {kTaggedBase, field_index.offset(), name,
257 field_type, MachineType::AnyTagged()};
258 if (access_mode == AccessMode::kLoad) {
259 if (field_type->Is(Type::UntaggedFloat64())) {
260 if (!field_index.is_inobject() || field_index.is_hidden_field() ||
261 !FLAG_unbox_double_fields) {
262 this_storage = this_effect =
263 graph()->NewNode(simplified()->LoadField(field_access),
264 this_storage, this_effect, this_control);
265 field_access.offset = HeapNumber::kValueOffset;
266 field_access.name = MaybeHandle<Name>();
267 }
268 field_access.machine_type = MachineType::Float64();
269 }
270 this_value = this_effect =
271 graph()->NewNode(simplified()->LoadField(field_access),
272 this_storage, this_effect, this_control);
273 } else {
274 DCHECK_EQ(AccessMode::kStore, access_mode);
275 if (field_type->Is(Type::UntaggedFloat64())) {
276 Node* check =
277 graph()->NewNode(simplified()->ObjectIsNumber(), this_value);
278 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
279 check, this_control);
280 exit_controls.push_back(
281 graph()->NewNode(common()->IfFalse(), branch));
282 this_control = graph()->NewNode(common()->IfTrue(), branch);
283 this_value = graph()->NewNode(common()->Guard(Type::Number()),
284 this_value, this_control);
285
286 if (!field_index.is_inobject() || field_index.is_hidden_field() ||
287 !FLAG_unbox_double_fields) {
288 if (access_info.HasTransitionMap()) {
289 // Allocate a MutableHeapNumber for the new property.
290 Callable callable =
291 CodeFactory::AllocateMutableHeapNumber(isolate());
292 CallDescriptor* desc = Linkage::GetStubCallDescriptor(
293 isolate(), jsgraph()->zone(), callable.descriptor(), 0,
294 CallDescriptor::kNoFlags, Operator::kNoThrow);
295 Node* this_box = this_effect = graph()->NewNode(
296 common()->Call(desc),
297 jsgraph()->HeapConstant(callable.code()),
298 jsgraph()->NoContextConstant(), this_effect, this_control);
299 this_effect = graph()->NewNode(
300 simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
301 this_box, this_value, this_effect, this_control);
302 this_value = this_box;
303
304 field_access.type = Type::TaggedPointer();
305 } else {
306 // We just store directly to the MutableHeapNumber.
307 this_storage = this_effect =
308 graph()->NewNode(simplified()->LoadField(field_access),
309 this_storage, this_effect, this_control);
310 field_access.offset = HeapNumber::kValueOffset;
311 field_access.name = MaybeHandle<Name>();
312 field_access.machine_type = MachineType::Float64();
313 }
314 } else {
315 // Unboxed double field, we store directly to the field.
316 field_access.machine_type = MachineType::Float64();
317 }
318 } else if (field_type->Is(Type::TaggedSigned())) {
319 Node* check =
320 graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
321 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
322 check, this_control);
323 exit_controls.push_back(
324 graph()->NewNode(common()->IfFalse(), branch));
325 this_control = graph()->NewNode(common()->IfTrue(), branch);
326 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi),
327 this_value, this_control);
328 } else if (field_type->Is(Type::TaggedPointer())) {
329 Node* check =
330 graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
331 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
332 check, this_control);
333 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
334 this_control = graph()->NewNode(common()->IfFalse(), branch);
335 if (field_type->NumClasses() > 0) {
336 // Emit a (sequence of) map checks for the value.
337 ZoneVector<Node*> this_controls(zone());
338 Node* this_value_map = this_effect = graph()->NewNode(
339 simplified()->LoadField(AccessBuilder::ForMap()), this_value,
340 this_effect, this_control);
341 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) {
342 Handle<Map> field_map(i.Current());
343 check = graph()->NewNode(
344 simplified()->ReferenceEqual(Type::Internal()),
345 this_value_map, jsgraph()->Constant(field_map));
346 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
347 check, this_control);
348 this_control = graph()->NewNode(common()->IfFalse(), branch);
349 this_controls.push_back(
350 graph()->NewNode(common()->IfTrue(), branch));
351 }
352 exit_controls.push_back(this_control);
353 int const this_control_count =
354 static_cast<int>(this_controls.size());
355 this_control =
356 (this_control_count == 1)
357 ? this_controls.front()
358 : graph()->NewNode(common()->Merge(this_control_count),
359 this_control_count,
360 &this_controls.front());
361 }
362 } else {
363 DCHECK(field_type->Is(Type::Tagged()));
364 }
365 Handle<Map> transition_map;
366 if (access_info.transition_map().ToHandle(&transition_map)) {
367 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect);
368 this_effect = graph()->NewNode(
369 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver,
370 jsgraph()->Constant(transition_map), this_effect, this_control);
371 }
372 this_effect = graph()->NewNode(simplified()->StoreField(field_access),
373 this_storage, this_value, this_effect,
374 this_control);
375 if (access_info.HasTransitionMap()) {
376 this_effect =
377 graph()->NewNode(common()->FinishRegion(),
378 jsgraph()->UndefinedConstant(), this_effect);
379 }
380 }
381 }
382
383 // Remember the final state for this property access.
384 values.push_back(this_value);
385 effects.push_back(this_effect);
386 controls.push_back(this_control);
387 }
388
389 // Collect the fallthrough control as final "exit" control.
390 if (fallthrough_control != control) {
391 // Mark the last fallthrough branch as deferred.
392 MarkAsDeferred(fallthrough_control);
393 }
394 exit_controls.push_back(fallthrough_control);
395
396 // Also collect the "receiver is smi" control if we didn't handle the case of
397 // Number primitives in the polymorphic branches above.
398 if (receiverissmi_control != nullptr) {
399 // Mark the "receiver is smi" case as deferred.
400 MarkAsDeferred(receiverissmi_control);
401 DCHECK_EQ(exit_effect, receiverissmi_effect);
402 exit_controls.push_back(receiverissmi_control);
403 }
404
405 // Generate the single "exit" point, where we get if either all map/instance
406 // type checks failed, or one of the assumptions inside one of the cases
407 // failes (i.e. failing prototype chain check).
408 // TODO(bmeurer): Consider falling back to IC here if deoptimization is
409 // disabled.
410 int const exit_control_count = static_cast<int>(exit_controls.size());
411 Node* exit_control =
412 (exit_control_count == 1)
413 ? exit_controls.front()
414 : graph()->NewNode(common()->Merge(exit_control_count),
415 exit_control_count, &exit_controls.front());
416 Node* deoptimize =
417 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
418 frame_state, exit_effect, exit_control);
419 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
420 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
421
422 // Generate the final merge point for all (polymorphic) branches.
423 int const control_count = static_cast<int>(controls.size());
424 if (control_count == 0) {
425 value = effect = control = jsgraph()->Dead();
426 } else if (control_count == 1) {
427 value = values.front();
428 effect = effects.front();
429 control = controls.front();
430 } else {
431 control = graph()->NewNode(common()->Merge(control_count), control_count,
432 &controls.front());
433 values.push_back(control);
434 value = graph()->NewNode(
435 common()->Phi(MachineRepresentation::kTagged, control_count),
436 control_count + 1, &values.front());
437 effects.push_back(control);
438 effect = graph()->NewNode(common()->EffectPhi(control_count),
439 control_count + 1, &effects.front());
440 }
441 ReplaceWithValue(node, value, effect, control);
442 return Replace(value);
443 }
444
445
ReduceJSLoadNamed(Node * node)446 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
447 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
448 NamedAccess const& p = NamedAccessOf(node->op());
449 Node* const value = jsgraph()->Dead();
450
451 // Extract receiver maps from the LOAD_IC using the LoadICNexus.
452 MapHandleList receiver_maps;
453 if (!p.feedback().IsValid()) return NoChange();
454 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
455 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
456 DCHECK_LT(0, receiver_maps.length());
457
458 // Try to lower the named access based on the {receiver_maps}.
459 return ReduceNamedAccess(node, value, receiver_maps, p.name(),
460 AccessMode::kLoad, p.language_mode());
461 }
462
463
ReduceJSStoreNamed(Node * node)464 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
465 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
466 NamedAccess const& p = NamedAccessOf(node->op());
467 Node* const value = NodeProperties::GetValueInput(node, 1);
468
469 // Extract receiver maps from the STORE_IC using the StoreICNexus.
470 MapHandleList receiver_maps;
471 if (!p.feedback().IsValid()) return NoChange();
472 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
473 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
474 DCHECK_LT(0, receiver_maps.length());
475
476 // Try to lower the named access based on the {receiver_maps}.
477 return ReduceNamedAccess(node, value, receiver_maps, p.name(),
478 AccessMode::kStore, p.language_mode());
479 }
480
481
ReduceElementAccess(Node * node,Node * index,Node * value,MapHandleList const & receiver_maps,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)482 Reduction JSNativeContextSpecialization::ReduceElementAccess(
483 Node* node, Node* index, Node* value, MapHandleList const& receiver_maps,
484 AccessMode access_mode, LanguageMode language_mode,
485 KeyedAccessStoreMode store_mode) {
486 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
487 node->opcode() == IrOpcode::kJSStoreProperty);
488 Node* receiver = NodeProperties::GetValueInput(node, 0);
489 Node* context = NodeProperties::GetContextInput(node);
490 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
491 Node* effect = NodeProperties::GetEffectInput(node);
492 Node* control = NodeProperties::GetControlInput(node);
493
494 // Not much we can do if deoptimization support is disabled.
495 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
496
497 // TODO(bmeurer): Add support for non-standard stores.
498 if (store_mode != STANDARD_STORE) return NoChange();
499
500 // Retrieve the native context from the given {node}.
501 Handle<Context> native_context;
502 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange();
503
504 // Compute element access infos for the receiver maps.
505 AccessInfoFactory access_info_factory(dependencies(), native_context,
506 graph()->zone());
507 ZoneVector<ElementAccessInfo> access_infos(zone());
508 if (!access_info_factory.ComputeElementAccessInfos(receiver_maps, access_mode,
509 &access_infos)) {
510 return NoChange();
511 }
512
513 // Nothing to do if we have no non-deprecated maps.
514 if (access_infos.empty()) return NoChange();
515
516 // The final states for every polymorphic branch. We join them with
517 // Merge+Phi+EffectPhi at the bottom.
518 ZoneVector<Node*> values(zone());
519 ZoneVector<Node*> effects(zone());
520 ZoneVector<Node*> controls(zone());
521
522 // The list of "exiting" controls, which currently go to a single deoptimize.
523 // TODO(bmeurer): Consider using an IC as fallback.
524 Node* const exit_effect = effect;
525 ZoneVector<Node*> exit_controls(zone());
526
527 // Ensure that {receiver} is a heap object.
528 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
529 Node* branch =
530 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
531 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
532 control = graph()->NewNode(common()->IfFalse(), branch);
533
534 // Load the {receiver} map. The resulting effect is the dominating effect for
535 // all (polymorphic) branches.
536 Node* receiver_map = effect =
537 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
538 receiver, effect, control);
539
540 // Generate code for the various different element access patterns.
541 Node* fallthrough_control = control;
542 for (ElementAccessInfo const& access_info : access_infos) {
543 Node* this_receiver = receiver;
544 Node* this_value = value;
545 Node* this_index = index;
546 Node* this_effect;
547 Node* this_control;
548
549 // Perform map check on {receiver}.
550 Type* receiver_type = access_info.receiver_type();
551 bool receiver_is_jsarray = true;
552 {
553 ZoneVector<Node*> this_controls(zone());
554 ZoneVector<Node*> this_effects(zone());
555 for (auto i = access_info.receiver_type()->Classes(); !i.Done();
556 i.Advance()) {
557 Handle<Map> map = i.Current();
558 Node* check =
559 graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
560 receiver_map, jsgraph()->Constant(map));
561 Node* branch =
562 graph()->NewNode(common()->Branch(), check, fallthrough_control);
563 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
564 this_effects.push_back(effect);
565 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
566 if (!map->IsJSArrayMap()) receiver_is_jsarray = false;
567 }
568
569 // Generate possible elements kind transitions.
570 for (auto transition : access_info.transitions()) {
571 Handle<Map> transition_source = transition.first;
572 Handle<Map> transition_target = transition.second;
573
574 // Check if {receiver} has the specified {transition_source} map.
575 Node* check = graph()->NewNode(
576 simplified()->ReferenceEqual(Type::Any()), receiver_map,
577 jsgraph()->HeapConstant(transition_source));
578 Node* branch =
579 graph()->NewNode(common()->Branch(), check, fallthrough_control);
580
581 // Migrate {receiver} from {transition_source} to {transition_target}.
582 Node* transition_control = graph()->NewNode(common()->IfTrue(), branch);
583 Node* transition_effect = effect;
584 if (IsSimpleMapChangeTransition(transition_source->elements_kind(),
585 transition_target->elements_kind())) {
586 // In-place migration, just store the {transition_target} map.
587 transition_effect = graph()->NewNode(
588 simplified()->StoreField(AccessBuilder::ForMap()), receiver,
589 jsgraph()->HeapConstant(transition_target), transition_effect,
590 transition_control);
591 } else {
592 // Instance migration, let the stub deal with the {receiver}.
593 TransitionElementsKindStub stub(isolate(),
594 transition_source->elements_kind(),
595 transition_target->elements_kind(),
596 transition_source->IsJSArrayMap());
597 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
598 isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 0,
599 CallDescriptor::kNeedsFrameState, node->op()->properties());
600 transition_effect = graph()->NewNode(
601 common()->Call(desc), jsgraph()->HeapConstant(stub.GetCode()),
602 receiver, jsgraph()->HeapConstant(transition_target), context,
603 frame_state, transition_effect, transition_control);
604 }
605 this_controls.push_back(transition_control);
606 this_effects.push_back(transition_effect);
607
608 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
609 }
610
611 // Create single chokepoint for the control.
612 int const this_control_count = static_cast<int>(this_controls.size());
613 if (this_control_count == 1) {
614 this_control = this_controls.front();
615 this_effect = this_effects.front();
616 } else {
617 this_control =
618 graph()->NewNode(common()->Merge(this_control_count),
619 this_control_count, &this_controls.front());
620 this_effects.push_back(this_control);
621 this_effect =
622 graph()->NewNode(common()->EffectPhi(this_control_count),
623 this_control_count + 1, &this_effects.front());
624 }
625 }
626
627 // Certain stores need a prototype chain check because shape changes
628 // could allow callbacks on elements in the prototype chain that are
629 // not compatible with (monomorphic) keyed stores.
630 Handle<JSObject> holder;
631 if (access_info.holder().ToHandle(&holder)) {
632 AssumePrototypesStable(receiver_type, native_context, holder);
633 }
634
635 // Check that the {index} is actually a Number.
636 if (!NumberMatcher(this_index).HasValue()) {
637 Node* check =
638 graph()->NewNode(simplified()->ObjectIsNumber(), this_index);
639 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
640 check, this_control);
641 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
642 this_control = graph()->NewNode(common()->IfTrue(), branch);
643 this_index = graph()->NewNode(common()->Guard(Type::Number()), this_index,
644 this_control);
645 }
646
647 // Convert the {index} to an unsigned32 value and check if the result is
648 // equal to the original {index}.
649 if (!NumberMatcher(this_index).IsInRange(0.0, kMaxUInt32)) {
650 Node* this_index32 =
651 graph()->NewNode(simplified()->NumberToUint32(), this_index);
652 Node* check = graph()->NewNode(simplified()->NumberEqual(), this_index32,
653 this_index);
654 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
655 check, this_control);
656 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
657 this_control = graph()->NewNode(common()->IfTrue(), branch);
658 this_index = this_index32;
659 }
660
661 // TODO(bmeurer): We currently specialize based on elements kind. We should
662 // also be able to properly support strings and other JSObjects here.
663 ElementsKind elements_kind = access_info.elements_kind();
664
665 // Load the elements for the {receiver}.
666 Node* this_elements = this_effect = graph()->NewNode(
667 simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
668 this_receiver, this_effect, this_control);
669
670 // Don't try to store to a copy-on-write backing store.
671 if (access_mode == AccessMode::kStore &&
672 IsFastSmiOrObjectElementsKind(elements_kind)) {
673 Node* this_elements_map = this_effect =
674 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
675 this_elements, this_effect, this_control);
676 check = graph()->NewNode(
677 simplified()->ReferenceEqual(Type::Any()), this_elements_map,
678 jsgraph()->HeapConstant(factory()->fixed_array_map()));
679 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check,
680 this_control);
681 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
682 this_control = graph()->NewNode(common()->IfTrue(), branch);
683 }
684
685 // Load the length of the {receiver}.
686 Node* this_length = this_effect =
687 receiver_is_jsarray
688 ? graph()->NewNode(
689 simplified()->LoadField(
690 AccessBuilder::ForJSArrayLength(elements_kind)),
691 this_receiver, this_effect, this_control)
692 : graph()->NewNode(
693 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
694 this_elements, this_effect, this_control);
695
696 // Check that the {index} is in the valid range for the {receiver}.
697 Node* check = graph()->NewNode(simplified()->NumberLessThan(), this_index,
698 this_length);
699 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check,
700 this_control);
701 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
702 this_control = graph()->NewNode(common()->IfTrue(), branch);
703
704 // Compute the element access.
705 Type* element_type = Type::Any();
706 MachineType element_machine_type = MachineType::AnyTagged();
707 if (IsFastDoubleElementsKind(elements_kind)) {
708 element_type = type_cache_.kFloat64;
709 element_machine_type = MachineType::Float64();
710 } else if (IsFastSmiElementsKind(elements_kind)) {
711 element_type = type_cache_.kSmi;
712 }
713 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
714 element_type, element_machine_type};
715
716 // Access the actual element.
717 // TODO(bmeurer): Refactor this into separate methods or even a separate
718 // class that deals with the elements access.
719 if (access_mode == AccessMode::kLoad) {
720 // Compute the real element access type, which includes the hole in case
721 // of holey backing stores.
722 if (elements_kind == FAST_HOLEY_ELEMENTS ||
723 elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
724 element_access.type = Type::Union(
725 element_type,
726 Type::Constant(factory()->the_hole_value(), graph()->zone()),
727 graph()->zone());
728 }
729 // Perform the actual backing store access.
730 this_value = this_effect = graph()->NewNode(
731 simplified()->LoadElement(element_access), this_elements, this_index,
732 this_effect, this_control);
733 // Handle loading from holey backing stores correctly, by either mapping
734 // the hole to undefined if possible, or deoptimizing otherwise.
735 if (elements_kind == FAST_HOLEY_ELEMENTS ||
736 elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
737 // Perform the hole check on the result.
738 Node* check =
739 graph()->NewNode(simplified()->ReferenceEqual(element_access.type),
740 this_value, jsgraph()->TheHoleConstant());
741 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
742 check, this_control);
743 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
744 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
745 // Check if we are allowed to turn the hole into undefined.
746 Type* initial_holey_array_type = Type::Class(
747 handle(isolate()->get_initial_js_array_map(elements_kind)),
748 graph()->zone());
749 if (receiver_type->NowIs(initial_holey_array_type) &&
750 isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
751 // Add a code dependency on the array protector cell.
752 AssumePrototypesStable(receiver_type, native_context,
753 isolate()->initial_object_prototype());
754 dependencies()->AssumePropertyCell(factory()->array_protector());
755 // Turn the hole into undefined.
756 this_control =
757 graph()->NewNode(common()->Merge(2), if_true, if_false);
758 this_value = graph()->NewNode(
759 common()->Phi(MachineRepresentation::kTagged, 2),
760 jsgraph()->UndefinedConstant(), this_value, this_control);
761 element_type =
762 Type::Union(element_type, Type::Undefined(), graph()->zone());
763 } else {
764 // Deoptimize in case of the hole.
765 exit_controls.push_back(if_true);
766 this_control = if_false;
767 }
768 // Rename the result to represent the actual type (not polluted by the
769 // hole).
770 this_value = graph()->NewNode(common()->Guard(element_type), this_value,
771 this_control);
772 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
773 // Perform the hole check on the result.
774 Node* check =
775 graph()->NewNode(simplified()->NumberIsHoleNaN(), this_value);
776 // Check if we are allowed to return the hole directly.
777 Type* initial_holey_array_type = Type::Class(
778 handle(isolate()->get_initial_js_array_map(elements_kind)),
779 graph()->zone());
780 if (receiver_type->NowIs(initial_holey_array_type) &&
781 isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
782 // Add a code dependency on the array protector cell.
783 AssumePrototypesStable(receiver_type, native_context,
784 isolate()->initial_object_prototype());
785 dependencies()->AssumePropertyCell(factory()->array_protector());
786 // Turn the hole into undefined.
787 this_value = graph()->NewNode(
788 common()->Select(MachineRepresentation::kTagged,
789 BranchHint::kFalse),
790 check, jsgraph()->UndefinedConstant(), this_value);
791 } else {
792 // Deoptimize in case of the hole.
793 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
794 check, this_control);
795 this_control = graph()->NewNode(common()->IfFalse(), branch);
796 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
797 }
798 }
799 } else {
800 DCHECK_EQ(AccessMode::kStore, access_mode);
801 if (IsFastSmiElementsKind(elements_kind)) {
802 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
803 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
804 check, this_control);
805 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
806 this_control = graph()->NewNode(common()->IfTrue(), branch);
807 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi),
808 this_value, this_control);
809 } else if (IsFastDoubleElementsKind(elements_kind)) {
810 Node* check =
811 graph()->NewNode(simplified()->ObjectIsNumber(), this_value);
812 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
813 check, this_control);
814 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
815 this_control = graph()->NewNode(common()->IfTrue(), branch);
816 this_value = graph()->NewNode(common()->Guard(Type::Number()),
817 this_value, this_control);
818 }
819 this_effect = graph()->NewNode(simplified()->StoreElement(element_access),
820 this_elements, this_index, this_value,
821 this_effect, this_control);
822 }
823
824 // Remember the final state for this element access.
825 values.push_back(this_value);
826 effects.push_back(this_effect);
827 controls.push_back(this_control);
828 }
829
830 // Collect the fallthrough control as final "exit" control.
831 if (fallthrough_control != control) {
832 // Mark the last fallthrough branch as deferred.
833 MarkAsDeferred(fallthrough_control);
834 }
835 exit_controls.push_back(fallthrough_control);
836
837 // Generate the single "exit" point, where we get if either all map/instance
838 // type checks failed, or one of the assumptions inside one of the cases
839 // failes (i.e. failing prototype chain check).
840 // TODO(bmeurer): Consider falling back to IC here if deoptimization is
841 // disabled.
842 int const exit_control_count = static_cast<int>(exit_controls.size());
843 Node* exit_control =
844 (exit_control_count == 1)
845 ? exit_controls.front()
846 : graph()->NewNode(common()->Merge(exit_control_count),
847 exit_control_count, &exit_controls.front());
848 Node* deoptimize =
849 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
850 frame_state, exit_effect, exit_control);
851 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
852 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
853
854 // Generate the final merge point for all (polymorphic) branches.
855 int const control_count = static_cast<int>(controls.size());
856 if (control_count == 0) {
857 value = effect = control = jsgraph()->Dead();
858 } else if (control_count == 1) {
859 value = values.front();
860 effect = effects.front();
861 control = controls.front();
862 } else {
863 control = graph()->NewNode(common()->Merge(control_count), control_count,
864 &controls.front());
865 values.push_back(control);
866 value = graph()->NewNode(
867 common()->Phi(MachineRepresentation::kTagged, control_count),
868 control_count + 1, &values.front());
869 effects.push_back(control);
870 effect = graph()->NewNode(common()->EffectPhi(control_count),
871 control_count + 1, &effects.front());
872 }
873 ReplaceWithValue(node, value, effect, control);
874 return Replace(value);
875 }
876
877
ReduceKeyedAccess(Node * node,Node * index,Node * value,FeedbackNexus const & nexus,AccessMode access_mode,LanguageMode language_mode,KeyedAccessStoreMode store_mode)878 Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
879 Node* node, Node* index, Node* value, FeedbackNexus const& nexus,
880 AccessMode access_mode, LanguageMode language_mode,
881 KeyedAccessStoreMode store_mode) {
882 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
883 node->opcode() == IrOpcode::kJSStoreProperty);
884
885 // Extract receiver maps from the {nexus}.
886 MapHandleList receiver_maps;
887 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
888 DCHECK_LT(0, receiver_maps.length());
889
890 // Optimize access for constant {index}.
891 HeapObjectMatcher mindex(index);
892 if (mindex.HasValue() && mindex.Value()->IsPrimitive()) {
893 // Keyed access requires a ToPropertyKey on the {index} first before
894 // looking up the property on the object (see ES6 section 12.3.2.1).
895 // We can only do this for non-observable ToPropertyKey invocations,
896 // so we limit the constant indices to primitives at this point.
897 Handle<Name> name;
898 if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) {
899 uint32_t array_index;
900 if (name->AsArrayIndex(&array_index)) {
901 // Use the constant array index.
902 index = jsgraph()->Constant(static_cast<double>(array_index));
903 } else {
904 name = factory()->InternalizeName(name);
905 return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
906 language_mode);
907 }
908 }
909 }
910
911 // Check if we have feedback for a named access.
912 if (Name* name = nexus.FindFirstName()) {
913 return ReduceNamedAccess(node, value, receiver_maps,
914 handle(name, isolate()), access_mode,
915 language_mode, index);
916 }
917
918 // Try to lower the element access based on the {receiver_maps}.
919 return ReduceElementAccess(node, index, value, receiver_maps, access_mode,
920 language_mode, store_mode);
921 }
922
923
ReduceJSLoadProperty(Node * node)924 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
925 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
926 PropertyAccess const& p = PropertyAccessOf(node->op());
927 Node* const index = NodeProperties::GetValueInput(node, 1);
928 Node* const value = jsgraph()->Dead();
929
930 // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus.
931 if (!p.feedback().IsValid()) return NoChange();
932 KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
933
934 // Try to lower the keyed access based on the {nexus}.
935 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad,
936 p.language_mode(), STANDARD_STORE);
937 }
938
939
ReduceJSStoreProperty(Node * node)940 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
941 DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode());
942 PropertyAccess const& p = PropertyAccessOf(node->op());
943 Node* const index = NodeProperties::GetValueInput(node, 1);
944 Node* const value = NodeProperties::GetValueInput(node, 2);
945
946 // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus.
947 if (!p.feedback().IsValid()) return NoChange();
948 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
949
950 // Extract the keyed access store mode from the KEYED_STORE_IC.
951 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode();
952
953 // Try to lower the keyed access based on the {nexus}.
954 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore,
955 p.language_mode(), store_mode);
956 }
957
958
AssumePrototypesStable(Type * receiver_type,Handle<Context> native_context,Handle<JSObject> holder)959 void JSNativeContextSpecialization::AssumePrototypesStable(
960 Type* receiver_type, Handle<Context> native_context,
961 Handle<JSObject> holder) {
962 // Determine actual holder and perform prototype chain checks.
963 for (auto i = receiver_type->Classes(); !i.Done(); i.Advance()) {
964 Handle<Map> map = i.Current();
965 // Perform the implicit ToObject for primitives here.
966 // Implemented according to ES6 section 7.3.2 GetV (V, P).
967 Handle<JSFunction> constructor;
968 if (Map::GetConstructorFunction(map, native_context)
969 .ToHandle(&constructor)) {
970 map = handle(constructor->initial_map(), isolate());
971 }
972 dependencies()->AssumePrototypeMapsStable(map, holder);
973 }
974 }
975
976
MarkAsDeferred(Node * if_projection)977 void JSNativeContextSpecialization::MarkAsDeferred(Node* if_projection) {
978 Node* branch = NodeProperties::GetControlInput(if_projection);
979 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
980 if (if_projection->opcode() == IrOpcode::kIfTrue) {
981 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse));
982 } else {
983 DCHECK_EQ(IrOpcode::kIfFalse, if_projection->opcode());
984 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue));
985 }
986 }
987
988
GetNativeContext(Node * node)989 MaybeHandle<Context> JSNativeContextSpecialization::GetNativeContext(
990 Node* node) {
991 Node* const context = NodeProperties::GetContextInput(node);
992 return NodeProperties::GetSpecializationNativeContext(context,
993 native_context());
994 }
995
996
graph() const997 Graph* JSNativeContextSpecialization::graph() const {
998 return jsgraph()->graph();
999 }
1000
1001
isolate() const1002 Isolate* JSNativeContextSpecialization::isolate() const {
1003 return jsgraph()->isolate();
1004 }
1005
1006
factory() const1007 Factory* JSNativeContextSpecialization::factory() const {
1008 return isolate()->factory();
1009 }
1010
1011
machine() const1012 MachineOperatorBuilder* JSNativeContextSpecialization::machine() const {
1013 return jsgraph()->machine();
1014 }
1015
1016
common() const1017 CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
1018 return jsgraph()->common();
1019 }
1020
1021
javascript() const1022 JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
1023 return jsgraph()->javascript();
1024 }
1025
1026
simplified() const1027 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
1028 return jsgraph()->simplified();
1029 }
1030
1031 } // namespace compiler
1032 } // namespace internal
1033 } // namespace v8
1034