| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/flow_graph_builder.h" | 9 #include "vm/flow_graph_builder.h" |
| 10 #include "vm/hash_map.h" | 10 #include "vm/hash_map.h" |
| (...skipping 517 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 528 field = cls.LookupInstanceField(field_name); | 528 field = cls.LookupInstanceField(field_name); |
| 529 if (!field.IsNull()) { | 529 if (!field.IsNull()) { |
| 530 return field.raw(); | 530 return field.raw(); |
| 531 } | 531 } |
| 532 cls = cls.SuperClass(); | 532 cls = cls.SuperClass(); |
| 533 } | 533 } |
| 534 return Field::null(); | 534 return Field::null(); |
| 535 } | 535 } |
| 536 | 536 |
| 537 | 537 |
| 538 // Use CHA to determine if the call needs a class check: if the callee's |
| 539 // receiver is the same as the caller's receiver and there are no overriden |
| 540 // callee functions, then no class check is needed. |
| 541 bool FlowGraphOptimizer::InstanceCallNeedsClassCheck( |
| 542 InstanceCallInstr* call) const { |
| 543 if (!FLAG_use_cha) return true; |
| 544 Definition* callee_receiver = call->ArgumentAt(0)->value()->definition(); |
| 545 ASSERT(callee_receiver != NULL); |
| 546 const Function& function = flow_graph_->parsed_function().function(); |
| 547 if (function.IsDynamicFunction() && |
| 548 callee_receiver->IsParameter() && |
| 549 (callee_receiver->AsParameter()->index() == 0)) { |
| 550 const intptr_t static_receiver_cid = Class::Handle(function.Owner()).id(); |
| 551 ZoneGrowableArray<intptr_t>* subclass_cids = |
| 552 CHA::GetSubclassIdsOf(static_receiver_cid); |
| 553 if (subclass_cids->is_empty()) { |
| 554 // No subclasses, no check needed. |
| 555 return false; |
| 556 } |
| 557 ZoneGrowableArray<Function*>* overriding_functions = |
| 558 CHA::GetNamedInstanceFunctionsOf(*subclass_cids, call->function_name()); |
| 559 if (overriding_functions->is_empty()) { |
| 560 // No overriding functions. |
| 561 return false; |
| 562 } |
| 563 } |
| 564 return true; |
| 565 } |
| 566 |
| 538 // Only unique implicit instance getters can be currently handled. | 567 // Only unique implicit instance getters can be currently handled. |
| 539 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { | 568 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { |
| 540 ASSERT(call->HasICData()); | 569 ASSERT(call->HasICData()); |
| 541 const ICData& ic_data = *call->ic_data(); | 570 const ICData& ic_data = *call->ic_data(); |
| 542 if (ic_data.NumberOfChecks() == 0) { | 571 if (ic_data.NumberOfChecks() == 0) { |
| 543 // No type feedback collected. | 572 // No type feedback collected. |
| 544 return false; | 573 return false; |
| 545 } | 574 } |
| 546 Function& target = Function::Handle(); | 575 Function& target = Function::Handle(); |
| 547 GrowableArray<intptr_t> class_ids; | 576 GrowableArray<intptr_t> class_ids; |
| 548 ic_data.GetCheckAt(0, &class_ids, &target); | 577 ic_data.GetCheckAt(0, &class_ids, &target); |
| 549 ASSERT(class_ids.length() == 1); | 578 ASSERT(class_ids.length() == 1); |
| 550 | 579 |
| 551 if (target.kind() == RawFunction::kImplicitGetter) { | 580 if (target.kind() == RawFunction::kImplicitGetter) { |
| 552 if (!HasOneTarget(ic_data)) { | 581 if (!HasOneTarget(ic_data)) { |
| 553 // TODO(srdjan): Implement for mutiple targets. | 582 // TODO(srdjan): Implement for mutiple targets. |
| 554 return false; | 583 return false; |
| 555 } | 584 } |
| 556 // Inline implicit instance getter. | 585 // Inline implicit instance getter. |
| 557 const String& field_name = | 586 const String& field_name = |
| 558 String::Handle(Field::NameFromGetter(call->function_name())); | 587 String::Handle(Field::NameFromGetter(call->function_name())); |
| 559 const Field& field = Field::Handle(GetField(class_ids[0], field_name)); | 588 const Field& field = Field::Handle(GetField(class_ids[0], field_name)); |
| 560 ASSERT(!field.IsNull()); | 589 ASSERT(!field.IsNull()); |
| 561 | 590 |
| 562 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | 591 if (InstanceCallNeedsClassCheck(call)) { |
| 592 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); |
| 593 } |
| 563 // Detach environment from the original instruction because it can't | 594 // Detach environment from the original instruction because it can't |
| 564 // deoptimize. | 595 // deoptimize. |
| 565 call->set_env(NULL); | 596 call->set_env(NULL); |
| 566 LoadInstanceFieldInstr* load = | 597 LoadInstanceFieldInstr* load = |
| 567 new LoadInstanceFieldInstr(field, call->ArgumentAt(0)->value()); | 598 new LoadInstanceFieldInstr(field, call->ArgumentAt(0)->value()); |
| 568 call->ReplaceWith(load, current_iterator()); | 599 call->ReplaceWith(load, current_iterator()); |
| 569 RemovePushArguments(call); | 600 RemovePushArguments(call); |
| 570 return true; | 601 return true; |
| 571 } | 602 } |
| 572 | 603 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 600 LoadVMFieldInstr* load = new LoadVMFieldInstr( | 631 LoadVMFieldInstr* load = new LoadVMFieldInstr( |
| 601 call->ArgumentAt(0)->value(), | 632 call->ArgumentAt(0)->value(), |
| 602 length_offset, | 633 length_offset, |
| 603 Type::ZoneHandle(Type::SmiType())); | 634 Type::ZoneHandle(Type::SmiType())); |
| 604 load->set_result_cid(kSmiCid); | 635 load->set_result_cid(kSmiCid); |
| 605 call->ReplaceWith(load, current_iterator()); | 636 call->ReplaceWith(load, current_iterator()); |
| 606 RemovePushArguments(call); | 637 RemovePushArguments(call); |
| 607 return true; | 638 return true; |
| 608 } | 639 } |
| 609 | 640 |
| 641 if (recognized_kind == MethodRecognizer::kGrowableArrayCapacity) { |
| 642 // Check receiver class. |
| 643 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); |
| 644 |
| 645 // TODO(srdjan): type of load should be GrowableObjectArrayType. |
| 646 LoadVMFieldInstr* data_load = new LoadVMFieldInstr( |
| 647 call->ArgumentAt(0)->value(), |
| 648 Array::data_offset(), |
| 649 Type::ZoneHandle(Type::DynamicType())); |
| 650 data_load->set_result_cid(kGrowableObjectArrayCid); |
| 651 InsertBefore(call, data_load, NULL, Definition::kValue); |
| 652 |
| 653 LoadVMFieldInstr* length_load = new LoadVMFieldInstr( |
| 654 new Value(data_load), |
| 655 Array::length_offset(), |
| 656 Type::ZoneHandle(Type::SmiType())); |
| 657 length_load->set_result_cid(kSmiCid); |
| 658 |
| 659 call->ReplaceWith(length_load, current_iterator()); |
| 660 RemovePushArguments(call); |
| 661 return true; |
| 662 } |
| 663 |
| 610 if (recognized_kind == MethodRecognizer::kStringBaseLength) { | 664 if (recognized_kind == MethodRecognizer::kStringBaseLength) { |
| 611 if (!HasOneTarget(ic_data)) { | 665 if (!HasOneTarget(ic_data)) { |
| 612 // Target is not only StringBase_get_length. | 666 // Target is not only StringBase_get_length. |
| 613 return false; | 667 return false; |
| 614 } | 668 } |
| 615 // Check receiver class. | 669 // Check receiver class. |
| 616 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | 670 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); |
| 617 | 671 |
| 618 LoadVMFieldInstr* load = new LoadVMFieldInstr( | 672 LoadVMFieldInstr* load = new LoadVMFieldInstr( |
| 619 call->ArgumentAt(0)->value(), | 673 call->ArgumentAt(0)->value(), |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 681 return; | 735 return; |
| 682 } | 736 } |
| 683 if ((op_kind == Token::kSET) && TryInlineInstanceSetter(instr)) { | 737 if ((op_kind == Token::kSET) && TryInlineInstanceSetter(instr)) { |
| 684 return; | 738 return; |
| 685 } | 739 } |
| 686 if (TryInlineInstanceMethod(instr)) { | 740 if (TryInlineInstanceMethod(instr)) { |
| 687 return; | 741 return; |
| 688 } | 742 } |
| 689 const ICData& unary_checks = | 743 const ICData& unary_checks = |
| 690 ICData::ZoneHandle(instr->ic_data()->AsUnaryClassChecks()); | 744 ICData::ZoneHandle(instr->ic_data()->AsUnaryClassChecks()); |
| 691 if (FLAG_use_cha) { | 745 if (!InstanceCallNeedsClassCheck(instr)) { |
| 692 // Check if receiver can have only one target, in which case | 746 const bool call_with_checks = false; |
| 693 // we emit call without class checks. | 747 PolymorphicInstanceCallInstr* call = |
| 694 Definition* receiver = instr->ArgumentAt(0)->value()->definition(); | 748 new PolymorphicInstanceCallInstr(instr, unary_checks, |
| 695 ASSERT(receiver != NULL); | 749 call_with_checks); |
| 696 const Function& function = flow_graph_->parsed_function().function(); | 750 instr->ReplaceWith(call, current_iterator()); |
| 697 if (function.IsDynamicFunction() && | 751 return; |
| 698 receiver->IsParameter() && | |
| 699 (receiver->AsParameter()->index() == 0)) { | |
| 700 intptr_t static_receiver_cid = Class::Handle(function.Owner()).id(); | |
| 701 ZoneGrowableArray<intptr_t>* subclass_cids = | |
| 702 CHA::GetSubclassIdsOf(static_receiver_cid); | |
| 703 ZoneGrowableArray<Function*>* overriding_functions = | |
| 704 CHA::GetNamedInstanceFunctionsOf(*subclass_cids, | |
| 705 instr->function_name()); | |
| 706 if (overriding_functions->is_empty()) { | |
| 707 const bool call_with_checks = false; | |
| 708 PolymorphicInstanceCallInstr* call = | |
| 709 new PolymorphicInstanceCallInstr(instr, unary_checks, | |
| 710 call_with_checks); | |
| 711 instr->ReplaceWith(call, current_iterator()); | |
| 712 return; | |
| 713 } | |
| 714 } | |
| 715 } | 752 } |
| 716 const intptr_t kMaxChecks = 4; | 753 const intptr_t kMaxChecks = 4; |
| 717 if (instr->ic_data()->NumberOfChecks() <= kMaxChecks) { | 754 if (instr->ic_data()->NumberOfChecks() <= kMaxChecks) { |
| 718 bool call_with_checks; | 755 bool call_with_checks; |
| 719 // TODO(srdjan): Add check class instr for mixed smi/non-smi. | 756 // TODO(srdjan): Add check class instr for mixed smi/non-smi. |
| 720 if (HasOneTarget(unary_checks) && | 757 if (HasOneTarget(unary_checks) && |
| 721 (unary_checks.GetReceiverClassIdAt(0) != kSmiCid)) { | 758 (unary_checks.GetReceiverClassIdAt(0) != kSmiCid)) { |
| 722 // Type propagation has not run yet, we cannot eliminate the check. | 759 // Type propagation has not run yet, we cannot eliminate the check. |
| 723 AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy()); | 760 AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy()); |
| 724 // Call can still deoptimize, do not detach environment from instr. | 761 // Call can still deoptimize, do not detach environment from instr. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 // Not an implicit setter. | 806 // Not an implicit setter. |
| 770 // TODO(srdjan): Inline special setters. | 807 // TODO(srdjan): Inline special setters. |
| 771 return false; | 808 return false; |
| 772 } | 809 } |
| 773 // Inline implicit instance setter. | 810 // Inline implicit instance setter. |
| 774 const String& field_name = | 811 const String& field_name = |
| 775 String::Handle(Field::NameFromSetter(instr->function_name())); | 812 String::Handle(Field::NameFromSetter(instr->function_name())); |
| 776 const Field& field = Field::Handle(GetField(class_id, field_name)); | 813 const Field& field = Field::Handle(GetField(class_id, field_name)); |
| 777 ASSERT(!field.IsNull()); | 814 ASSERT(!field.IsNull()); |
| 778 | 815 |
| 779 AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy()); | 816 if (InstanceCallNeedsClassCheck(instr)) { |
| 817 AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy()); |
| 818 } |
| 780 // Detach environment from the original instruction because it can't | 819 // Detach environment from the original instruction because it can't |
| 781 // deoptimize. | 820 // deoptimize. |
| 782 instr->set_env(NULL); | 821 instr->set_env(NULL); |
| 783 StoreInstanceFieldInstr* store = new StoreInstanceFieldInstr( | 822 StoreInstanceFieldInstr* store = new StoreInstanceFieldInstr( |
| 784 field, | 823 field, |
| 785 instr->ArgumentAt(0)->value(), | 824 instr->ArgumentAt(0)->value(), |
| 786 instr->ArgumentAt(1)->value()); | 825 instr->ArgumentAt(1)->value()); |
| 787 instr->ReplaceWith(store, current_iterator()); | 826 instr->ReplaceWith(store, current_iterator()); |
| 788 RemovePushArguments(instr); | 827 RemovePushArguments(instr); |
| 789 return true; | 828 return true; |
| (...skipping 537 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1327 DirectChainedHashMap<Definition*> child_map(*map); // Copy map. | 1366 DirectChainedHashMap<Definition*> child_map(*map); // Copy map. |
| 1328 OptimizeRecursive(child, &child_map); | 1367 OptimizeRecursive(child, &child_map); |
| 1329 } else { | 1368 } else { |
| 1330 OptimizeRecursive(child, map); // Reuse map for the last child. | 1369 OptimizeRecursive(child, map); // Reuse map for the last child. |
| 1331 } | 1370 } |
| 1332 } | 1371 } |
| 1333 } | 1372 } |
| 1334 | 1373 |
| 1335 | 1374 |
| 1336 } // namespace dart | 1375 } // namespace dart |
| OLD | NEW |