OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
6 | 6 |
7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
(...skipping 27 matching lines...) Expand all Loading... |
38 return true; | 38 return true; |
39 } | 39 } |
40 | 40 |
41 bool HasOnlyNumberMaps(MapList const& maps) { | 41 bool HasOnlyNumberMaps(MapList const& maps) { |
42 for (auto map : maps) { | 42 for (auto map : maps) { |
43 if (map->instance_type() != HEAP_NUMBER_TYPE) return false; | 43 if (map->instance_type() != HEAP_NUMBER_TYPE) return false; |
44 } | 44 } |
45 return true; | 45 return true; |
46 } | 46 } |
47 | 47 |
48 bool HasOnlyStringMaps(MapList const& maps) { | 48 template <typename T> |
| 49 bool HasOnlyStringMaps(T const& maps) { |
49 for (auto map : maps) { | 50 for (auto map : maps) { |
50 if (!map->IsStringMap()) return false; | 51 if (!map->IsStringMap()) return false; |
51 } | 52 } |
52 return true; | 53 return true; |
53 } | 54 } |
54 | 55 |
55 } // namespace | 56 } // namespace |
56 | 57 |
57 JSNativeContextSpecialization::JSNativeContextSpecialization( | 58 JSNativeContextSpecialization::JSNativeContextSpecialization( |
58 Editor* editor, JSGraph* jsgraph, Flags flags, | 59 Editor* editor, JSGraph* jsgraph, Flags flags, |
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
419 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || | 420 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || |
420 node->opcode() == IrOpcode::kJSStoreProperty); | 421 node->opcode() == IrOpcode::kJSStoreProperty); |
421 Node* receiver = NodeProperties::GetValueInput(node, 0); | 422 Node* receiver = NodeProperties::GetValueInput(node, 0); |
422 Node* effect = NodeProperties::GetEffectInput(node); | 423 Node* effect = NodeProperties::GetEffectInput(node); |
423 Node* control = NodeProperties::GetControlInput(node); | 424 Node* control = NodeProperties::GetControlInput(node); |
424 Node* frame_state = NodeProperties::FindFrameStateBefore(node); | 425 Node* frame_state = NodeProperties::FindFrameStateBefore(node); |
425 | 426 |
426 // Not much we can do if deoptimization support is disabled. | 427 // Not much we can do if deoptimization support is disabled. |
427 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 428 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
428 | 429 |
429 // Retrieve the native context from the given {node}. | 430 // Check for keyed access to strings. |
430 Handle<Context> native_context; | 431 if (HasOnlyStringMaps(receiver_maps)) { |
431 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); | 432 // Strings are immutable in JavaScript. |
432 | 433 if (access_mode == AccessMode::kStore) return NoChange(); |
433 // Compute element access infos for the receiver maps. | 434 |
434 AccessInfoFactory access_info_factory(dependencies(), native_context, | 435 // Ensure that the {receiver} is actually a String. |
435 graph()->zone()); | 436 receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver, |
436 ZoneVector<ElementAccessInfo> access_infos(zone()); | 437 effect, control); |
437 if (!access_info_factory.ComputeElementAccessInfos(receiver_maps, access_mode, | 438 |
438 &access_infos)) { | 439 // Determine the {receiver} length. |
439 return NoChange(); | 440 Node* length = effect = graph()->NewNode( |
440 } | 441 simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, |
441 | 442 effect, control); |
442 // Nothing to do if we have no non-deprecated maps. | 443 |
443 if (access_infos.empty()) { | 444 // Ensure that {index} is less than {receiver} length. |
444 return ReduceSoftDeoptimize( | 445 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, |
445 node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); | 446 length, effect, control); |
446 } | 447 |
447 | 448 // Load the character from the {receiver}. |
448 // Ensure that {receiver} is a heap object. | 449 value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, index, |
449 effect = BuildCheckTaggedPointer(receiver, effect, control); | 450 control); |
450 | 451 |
451 // Check for the monomorphic case. | 452 // Return it as a single character string. |
452 if (access_infos.size() == 1) { | 453 value = graph()->NewNode(simplified()->StringFromCharCode(), value); |
453 ElementAccessInfo access_info = access_infos.front(); | 454 } else { |
454 | 455 // Retrieve the native context from the given {node}. |
455 // Perform possible elements kind transitions. | 456 Handle<Context> native_context; |
456 for (auto transition : access_info.transitions()) { | 457 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); |
457 Handle<Map> const transition_source = transition.first; | 458 |
458 Handle<Map> const transition_target = transition.second; | 459 // Compute element access infos for the receiver maps. |
459 effect = graph()->NewNode( | 460 AccessInfoFactory access_info_factory(dependencies(), native_context, |
460 simplified()->TransitionElementsKind( | 461 graph()->zone()); |
461 IsSimpleMapChangeTransition(transition_source->elements_kind(), | 462 ZoneVector<ElementAccessInfo> access_infos(zone()); |
462 transition_target->elements_kind()) | 463 if (!access_info_factory.ComputeElementAccessInfos( |
463 ? ElementsTransition::kFastTransition | 464 receiver_maps, access_mode, &access_infos)) { |
464 : ElementsTransition::kSlowTransition), | 465 return NoChange(); |
465 receiver, jsgraph()->HeapConstant(transition_source), | |
466 jsgraph()->HeapConstant(transition_target), effect, control); | |
467 } | 466 } |
468 | 467 |
469 // TODO(turbofan): The effect/control linearization will not find a | 468 // Nothing to do if we have no non-deprecated maps. |
470 // FrameState after the StoreField or Call that is generated for the | 469 if (access_infos.empty()) { |
471 // elements kind transition above. This is because those operators | 470 return ReduceSoftDeoptimize( |
472 // don't have the kNoWrite flag on it, even though they are not | 471 node, |
473 // observable by JavaScript. | 472 DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); |
474 effect = | 473 } |
475 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); | 474 |
476 | 475 // Ensure that {receiver} is a heap object. |
477 // Perform map check on the {receiver}. | 476 effect = BuildCheckTaggedPointer(receiver, effect, control); |
478 effect = | 477 |
479 BuildCheckMaps(receiver, effect, control, access_info.receiver_maps()); | 478 // Check for the monomorphic case. |
480 | 479 if (access_infos.size() == 1) { |
481 // Access the actual element. | 480 ElementAccessInfo access_info = access_infos.front(); |
482 ValueEffectControl continuation = BuildElementAccess( | |
483 receiver, index, value, effect, control, native_context, access_info, | |
484 access_mode, store_mode); | |
485 value = continuation.value(); | |
486 effect = continuation.effect(); | |
487 control = continuation.control(); | |
488 } else { | |
489 // The final states for every polymorphic branch. We join them with | |
490 // Merge+Phi+EffectPhi at the bottom. | |
491 ZoneVector<Node*> values(zone()); | |
492 ZoneVector<Node*> effects(zone()); | |
493 ZoneVector<Node*> controls(zone()); | |
494 | |
495 // Generate code for the various different element access patterns. | |
496 Node* fallthrough_control = control; | |
497 for (size_t j = 0; j < access_infos.size(); ++j) { | |
498 ElementAccessInfo const& access_info = access_infos[j]; | |
499 Node* this_receiver = receiver; | |
500 Node* this_value = value; | |
501 Node* this_index = index; | |
502 Node* this_effect = effect; | |
503 Node* this_control = fallthrough_control; | |
504 | 481 |
505 // Perform possible elements kind transitions. | 482 // Perform possible elements kind transitions. |
506 for (auto transition : access_info.transitions()) { | 483 for (auto transition : access_info.transitions()) { |
507 Handle<Map> const transition_source = transition.first; | 484 Handle<Map> const transition_source = transition.first; |
508 Handle<Map> const transition_target = transition.second; | 485 Handle<Map> const transition_target = transition.second; |
509 this_effect = graph()->NewNode( | 486 effect = graph()->NewNode( |
510 simplified()->TransitionElementsKind( | 487 simplified()->TransitionElementsKind( |
511 IsSimpleMapChangeTransition(transition_source->elements_kind(), | 488 IsSimpleMapChangeTransition(transition_source->elements_kind(), |
512 transition_target->elements_kind()) | 489 transition_target->elements_kind()) |
513 ? ElementsTransition::kFastTransition | 490 ? ElementsTransition::kFastTransition |
514 : ElementsTransition::kSlowTransition), | 491 : ElementsTransition::kSlowTransition), |
515 receiver, jsgraph()->HeapConstant(transition_source), | 492 receiver, jsgraph()->HeapConstant(transition_source), |
516 jsgraph()->HeapConstant(transition_target), this_effect, | 493 jsgraph()->HeapConstant(transition_target), effect, control); |
517 this_control); | |
518 } | 494 } |
519 | 495 |
520 // Load the {receiver} map. | 496 // TODO(turbofan): The effect/control linearization will not find a |
521 Node* receiver_map = this_effect = | 497 // FrameState after the StoreField or Call that is generated for the |
522 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 498 // elements kind transition above. This is because those operators |
523 receiver, this_effect, this_control); | 499 // don't have the kNoWrite flag on it, even though they are not |
524 | 500 // observable by JavaScript. |
525 // Perform map check(s) on {receiver}. | 501 effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, |
526 MapList const& receiver_maps = access_info.receiver_maps(); | 502 control); |
527 { | 503 |
528 ZoneVector<Node*> this_controls(zone()); | 504 // Perform map check on the {receiver}. |
529 ZoneVector<Node*> this_effects(zone()); | 505 effect = BuildCheckMaps(receiver, effect, control, |
530 size_t num_classes = receiver_maps.size(); | 506 access_info.receiver_maps()); |
531 for (Handle<Map> map : receiver_maps) { | 507 |
532 DCHECK_LT(0u, num_classes); | 508 // Access the actual element. |
533 Node* check = | 509 ValueEffectControl continuation = BuildElementAccess( |
534 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, | 510 receiver, index, value, effect, control, native_context, access_info, |
535 jsgraph()->Constant(map)); | 511 access_mode, store_mode); |
536 if (--num_classes == 0 && j == access_infos.size() - 1) { | 512 value = continuation.value(); |
537 // Last map check on the fallthrough control path, do a conditional | 513 effect = continuation.effect(); |
538 // eager deoptimization exit here. | 514 control = continuation.control(); |
539 // TODO(turbofan): This is ugly as hell! We should probably | 515 } else { |
540 // introduce macro-ish operators for property access that | 516 // The final states for every polymorphic branch. We join them with |
541 // encapsulate this whole mess. | 517 // Merge+Phi+EffectPhi at the bottom. |
542 check = graph()->NewNode(simplified()->CheckIf(), check, | 518 ZoneVector<Node*> values(zone()); |
543 this_effect, this_control); | 519 ZoneVector<Node*> effects(zone()); |
544 this_controls.push_back(this_control); | 520 ZoneVector<Node*> controls(zone()); |
545 this_effects.push_back(check); | 521 |
546 fallthrough_control = nullptr; | 522 // Generate code for the various different element access patterns. |
| 523 Node* fallthrough_control = control; |
| 524 for (size_t j = 0; j < access_infos.size(); ++j) { |
| 525 ElementAccessInfo const& access_info = access_infos[j]; |
| 526 Node* this_receiver = receiver; |
| 527 Node* this_value = value; |
| 528 Node* this_index = index; |
| 529 Node* this_effect = effect; |
| 530 Node* this_control = fallthrough_control; |
| 531 |
| 532 // Perform possible elements kind transitions. |
| 533 for (auto transition : access_info.transitions()) { |
| 534 Handle<Map> const transition_source = transition.first; |
| 535 Handle<Map> const transition_target = transition.second; |
| 536 this_effect = graph()->NewNode( |
| 537 simplified()->TransitionElementsKind( |
| 538 IsSimpleMapChangeTransition( |
| 539 transition_source->elements_kind(), |
| 540 transition_target->elements_kind()) |
| 541 ? ElementsTransition::kFastTransition |
| 542 : ElementsTransition::kSlowTransition), |
| 543 receiver, jsgraph()->HeapConstant(transition_source), |
| 544 jsgraph()->HeapConstant(transition_target), this_effect, |
| 545 this_control); |
| 546 } |
| 547 |
| 548 // Load the {receiver} map. |
| 549 Node* receiver_map = this_effect = |
| 550 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 551 receiver, this_effect, this_control); |
| 552 |
| 553 // Perform map check(s) on {receiver}. |
| 554 MapList const& receiver_maps = access_info.receiver_maps(); |
| 555 { |
| 556 ZoneVector<Node*> this_controls(zone()); |
| 557 ZoneVector<Node*> this_effects(zone()); |
| 558 size_t num_classes = receiver_maps.size(); |
| 559 for (Handle<Map> map : receiver_maps) { |
| 560 DCHECK_LT(0u, num_classes); |
| 561 Node* check = |
| 562 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, |
| 563 jsgraph()->Constant(map)); |
| 564 if (--num_classes == 0 && j == access_infos.size() - 1) { |
| 565 // Last map check on the fallthrough control path, do a |
| 566 // conditional eager deoptimization exit here. |
| 567 // TODO(turbofan): This is ugly as hell! We should probably |
| 568 // introduce macro-ish operators for property access that |
| 569 // encapsulate this whole mess. |
| 570 check = graph()->NewNode(simplified()->CheckIf(), check, |
| 571 this_effect, this_control); |
| 572 this_controls.push_back(this_control); |
| 573 this_effects.push_back(check); |
| 574 fallthrough_control = nullptr; |
| 575 } else { |
| 576 Node* branch = graph()->NewNode(common()->Branch(), check, |
| 577 fallthrough_control); |
| 578 this_controls.push_back( |
| 579 graph()->NewNode(common()->IfTrue(), branch)); |
| 580 this_effects.push_back(effect); |
| 581 fallthrough_control = |
| 582 graph()->NewNode(common()->IfFalse(), branch); |
| 583 } |
| 584 } |
| 585 |
| 586 // Create single chokepoint for the control. |
| 587 int const this_control_count = static_cast<int>(this_controls.size()); |
| 588 if (this_control_count == 1) { |
| 589 this_control = this_controls.front(); |
| 590 this_effect = this_effects.front(); |
547 } else { | 591 } else { |
548 Node* branch = graph()->NewNode(common()->Branch(), check, | 592 this_control = |
549 fallthrough_control); | 593 graph()->NewNode(common()->Merge(this_control_count), |
550 this_controls.push_back( | 594 this_control_count, &this_controls.front()); |
551 graph()->NewNode(common()->IfTrue(), branch)); | 595 this_effects.push_back(this_control); |
552 this_effects.push_back(effect); | 596 this_effect = |
553 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 597 graph()->NewNode(common()->EffectPhi(this_control_count), |
| 598 this_control_count + 1, &this_effects.front()); |
| 599 |
| 600 // TODO(turbofan): The effect/control linearization will not find a |
| 601 // FrameState after the EffectPhi that is generated above. |
| 602 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
| 603 this_effect, this_control); |
554 } | 604 } |
555 } | 605 } |
556 | 606 |
557 // Create single chokepoint for the control. | 607 // Access the actual element. |
558 int const this_control_count = static_cast<int>(this_controls.size()); | 608 ValueEffectControl continuation = BuildElementAccess( |
559 if (this_control_count == 1) { | 609 this_receiver, this_index, this_value, this_effect, this_control, |
560 this_control = this_controls.front(); | 610 native_context, access_info, access_mode, store_mode); |
561 this_effect = this_effects.front(); | 611 values.push_back(continuation.value()); |
562 } else { | 612 effects.push_back(continuation.effect()); |
563 this_control = | 613 controls.push_back(continuation.control()); |
564 graph()->NewNode(common()->Merge(this_control_count), | |
565 this_control_count, &this_controls.front()); | |
566 this_effects.push_back(this_control); | |
567 this_effect = | |
568 graph()->NewNode(common()->EffectPhi(this_control_count), | |
569 this_control_count + 1, &this_effects.front()); | |
570 | |
571 // TODO(turbofan): The effect/control linearization will not find a | |
572 // FrameState after the EffectPhi that is generated above. | |
573 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, | |
574 this_effect, this_control); | |
575 } | |
576 } | 614 } |
577 | 615 |
578 // Access the actual element. | 616 DCHECK_NULL(fallthrough_control); |
579 ValueEffectControl continuation = BuildElementAccess( | 617 |
580 this_receiver, this_index, this_value, this_effect, this_control, | 618 // Generate the final merge point for all (polymorphic) branches. |
581 native_context, access_info, access_mode, store_mode); | 619 int const control_count = static_cast<int>(controls.size()); |
582 values.push_back(continuation.value()); | 620 if (control_count == 0) { |
583 effects.push_back(continuation.effect()); | 621 value = effect = control = jsgraph()->Dead(); |
584 controls.push_back(continuation.control()); | 622 } else if (control_count == 1) { |
585 } | 623 value = values.front(); |
586 | 624 effect = effects.front(); |
587 DCHECK_NULL(fallthrough_control); | 625 control = controls.front(); |
588 | 626 } else { |
589 // Generate the final merge point for all (polymorphic) branches. | 627 control = graph()->NewNode(common()->Merge(control_count), |
590 int const control_count = static_cast<int>(controls.size()); | 628 control_count, &controls.front()); |
591 if (control_count == 0) { | 629 values.push_back(control); |
592 value = effect = control = jsgraph()->Dead(); | 630 value = graph()->NewNode( |
593 } else if (control_count == 1) { | 631 common()->Phi(MachineRepresentation::kTagged, control_count), |
594 value = values.front(); | 632 control_count + 1, &values.front()); |
595 effect = effects.front(); | 633 effects.push_back(control); |
596 control = controls.front(); | 634 effect = graph()->NewNode(common()->EffectPhi(control_count), |
597 } else { | 635 control_count + 1, &effects.front()); |
598 control = graph()->NewNode(common()->Merge(control_count), control_count, | 636 } |
599 &controls.front()); | |
600 values.push_back(control); | |
601 value = graph()->NewNode( | |
602 common()->Phi(MachineRepresentation::kTagged, control_count), | |
603 control_count + 1, &values.front()); | |
604 effects.push_back(control); | |
605 effect = graph()->NewNode(common()->EffectPhi(control_count), | |
606 control_count + 1, &effects.front()); | |
607 } | 637 } |
608 } | 638 } |
609 | 639 |
610 ReplaceWithValue(node, value, effect, control); | 640 ReplaceWithValue(node, value, effect, control); |
611 return Replace(value); | 641 return Replace(value); |
612 } | 642 } |
613 | 643 |
614 template <typename KeyedICNexus> | 644 template <typename KeyedICNexus> |
615 Reduction JSNativeContextSpecialization::ReduceKeyedAccess( | 645 Reduction JSNativeContextSpecialization::ReduceKeyedAccess( |
616 Node* node, Node* index, Node* value, KeyedICNexus const& nexus, | 646 Node* node, Node* index, Node* value, KeyedICNexus const& nexus, |
(...skipping 800 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1417 } | 1447 } |
1418 | 1448 |
1419 | 1449 |
1420 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 1450 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
1421 return jsgraph()->simplified(); | 1451 return jsgraph()->simplified(); |
1422 } | 1452 } |
1423 | 1453 |
1424 } // namespace compiler | 1454 } // namespace compiler |
1425 } // namespace internal | 1455 } // namespace internal |
1426 } // namespace v8 | 1456 } // namespace v8 |
OLD | NEW |