| 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 |