| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
| 9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
| 10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 "Maximum number of polymorphic check, otherwise it is megamorphic."); | 37 "Maximum number of polymorphic check, otherwise it is megamorphic."); |
| 38 DEFINE_FLAG(int, max_equality_polymorphic_checks, 32, | 38 DEFINE_FLAG(int, max_equality_polymorphic_checks, 32, |
| 39 "Maximum number of polymorphic checks in equality operator," | 39 "Maximum number of polymorphic checks in equality operator," |
| 40 " otherwise use megamorphic dispatch."); | 40 " otherwise use megamorphic dispatch."); |
| 41 DEFINE_FLAG(bool, merge_sin_cos, false, "Merge sin/cos into sincos"); | 41 DEFINE_FLAG(bool, merge_sin_cos, false, "Merge sin/cos into sincos"); |
| 42 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); | 42 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); |
| 43 DEFINE_FLAG(bool, truncating_left_shift, true, | 43 DEFINE_FLAG(bool, truncating_left_shift, true, |
| 44 "Optimize left shift to truncate if possible"); | 44 "Optimize left shift to truncate if possible"); |
| 45 DEFINE_FLAG(bool, use_cha_deopt, true, | 45 DEFINE_FLAG(bool, use_cha_deopt, true, |
| 46 "Use class hierarchy analysis even if it can cause deoptimization."); | 46 "Use class hierarchy analysis even if it can cause deoptimization."); |
| 47 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32) | |
| 48 DEFINE_FLAG(bool, trace_smi_widening, false, "Trace Smi->Int32 widening pass."); | |
| 49 #endif | |
| 50 | 47 |
| 51 DECLARE_FLAG(bool, precompilation); | 48 DECLARE_FLAG(bool, precompilation); |
| 52 DECLARE_FLAG(bool, polymorphic_with_deopt); | 49 DECLARE_FLAG(bool, polymorphic_with_deopt); |
| 53 DECLARE_FLAG(bool, trace_cha); | 50 DECLARE_FLAG(bool, trace_cha); |
| 54 DECLARE_FLAG(bool, trace_field_guards); | 51 DECLARE_FLAG(bool, trace_field_guards); |
| 55 | 52 |
| 56 // Quick access to the current isolate and zone. | 53 // Quick access to the current isolate and zone. |
| 57 #define I (isolate()) | 54 #define I (isolate()) |
| 58 #define Z (zone()) | 55 #define Z (zone()) |
| 59 | 56 |
| 60 static bool ShouldInlineSimd() { | 57 static bool ShouldInlineSimd() { |
| 61 return FlowGraphCompiler::SupportsUnboxedSimd128(); | 58 return FlowGraphCompiler::SupportsUnboxedSimd128(); |
| 62 } | 59 } |
| 63 | 60 |
| 64 | 61 |
| 65 static bool CanUnboxDouble() { | 62 static bool CanUnboxDouble() { |
| 66 return FlowGraphCompiler::SupportsUnboxedDoubles(); | 63 return FlowGraphCompiler::SupportsUnboxedDoubles(); |
| 67 } | 64 } |
| 68 | 65 |
| 69 | 66 |
| 70 static bool CanConvertUnboxedMintToDouble() { | 67 static bool CanConvertUnboxedMintToDouble() { |
| 71 #if defined(TARGET_ARCH_IA32) | 68 return FlowGraphCompiler::CanConvertUnboxedMintToDouble(); |
| 72 return true; | |
| 73 #else | |
| 74 // ARM does not have a short instruction sequence for converting int64 to | |
| 75 // double. | |
| 76 // TODO(johnmccutchan): Investigate possibility on MIPS once | |
| 77 // mints are implemented there. | |
| 78 return false; | |
| 79 #endif | |
| 80 } | 69 } |
| 81 | 70 |
| 82 | 71 |
| 83 // Optimize instance calls using ICData. | 72 // Optimize instance calls using ICData. |
| 84 void FlowGraphOptimizer::ApplyICData() { | 73 void FlowGraphOptimizer::ApplyICData() { |
| 85 VisitBlocks(); | 74 VisitBlocks(); |
| 86 } | 75 } |
| 87 | 76 |
| 88 | 77 |
| 89 void FlowGraphOptimizer::PopulateWithICData() { | 78 void FlowGraphOptimizer::PopulateWithICData() { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 Instruction* instr = it.Current(); | 120 Instruction* instr = it.Current(); |
| 132 if (instr->IsInstanceCall()) { | 121 if (instr->IsInstanceCall()) { |
| 133 InstanceCallInstr* call = instr->AsInstanceCall(); | 122 InstanceCallInstr* call = instr->AsInstanceCall(); |
| 134 if (call->HasICData()) { | 123 if (call->HasICData()) { |
| 135 if (TryCreateICData(call)) { | 124 if (TryCreateICData(call)) { |
| 136 VisitInstanceCall(call); | 125 VisitInstanceCall(call); |
| 137 } | 126 } |
| 138 } | 127 } |
| 139 } else if (instr->IsPolymorphicInstanceCall()) { | 128 } else if (instr->IsPolymorphicInstanceCall()) { |
| 140 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall()); | 129 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall()); |
| 141 } else if (instr->IsStrictCompare()) { | |
| 142 VisitStrictCompare(instr->AsStrictCompare()); | |
| 143 } else if (instr->IsBranch()) { | |
| 144 ComparisonInstr* compare = instr->AsBranch()->comparison(); | |
| 145 if (compare->IsStrictCompare()) { | |
| 146 VisitStrictCompare(compare->AsStrictCompare()); | |
| 147 } | |
| 148 } | 130 } |
| 149 } | 131 } |
| 150 current_iterator_ = NULL; | 132 current_iterator_ = NULL; |
| 151 } | 133 } |
| 152 } | 134 } |
| 153 | 135 |
| 154 | 136 |
| 155 // TODO(srdjan): Test/support other number types as well. | 137 // TODO(srdjan): Test/support other number types as well. |
| 156 static bool IsNumberCid(intptr_t cid) { | 138 static bool IsNumberCid(intptr_t cid) { |
| 157 return (cid == kSmiCid) || (cid == kDoubleCid); | 139 return (cid == kSmiCid) || (cid == kDoubleCid); |
| (...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 } | 578 } |
| 597 } | 579 } |
| 598 } | 580 } |
| 599 TryMergeTruncDivMod(&div_mod_merge); | 581 TryMergeTruncDivMod(&div_mod_merge); |
| 600 TryMergeMathUnary(&sin_cos_merge); | 582 TryMergeMathUnary(&sin_cos_merge); |
| 601 current_iterator_ = NULL; | 583 current_iterator_ = NULL; |
| 602 } | 584 } |
| 603 } | 585 } |
| 604 | 586 |
| 605 | 587 |
| 606 bool FlowGraphOptimizer::Canonicalize() { | |
| 607 bool changed = false; | |
| 608 | |
| 609 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 610 !block_it.Done(); | |
| 611 block_it.Advance()) { | |
| 612 for (ForwardInstructionIterator it(block_it.Current()); | |
| 613 !it.Done(); | |
| 614 it.Advance()) { | |
| 615 Instruction* current = it.Current(); | |
| 616 if (current->HasUnmatchedInputRepresentations()) { | |
| 617 // Can't canonicalize this instruction until all conversions for its | |
| 618 // inputs are inserted. | |
| 619 continue; | |
| 620 } | |
| 621 | |
| 622 Instruction* replacement = current->Canonicalize(flow_graph()); | |
| 623 | |
| 624 if (replacement != current) { | |
| 625 // For non-definitions Canonicalize should return either NULL or | |
| 626 // this. | |
| 627 ASSERT((replacement == NULL) || current->IsDefinition()); | |
| 628 flow_graph_->ReplaceCurrentInstruction(&it, current, replacement); | |
| 629 changed = true; | |
| 630 } | |
| 631 } | |
| 632 } | |
| 633 return changed; | |
| 634 } | |
| 635 | |
| 636 | |
| 637 static bool IsUnboxedInteger(Representation rep) { | |
| 638 return (rep == kUnboxedInt32) || | |
| 639 (rep == kUnboxedUint32) || | |
| 640 (rep == kUnboxedMint); | |
| 641 } | |
| 642 | |
| 643 | |
| 644 void FlowGraphOptimizer::InsertConversion(Representation from, | |
| 645 Representation to, | |
| 646 Value* use, | |
| 647 bool is_environment_use) { | |
| 648 Instruction* insert_before; | |
| 649 Instruction* deopt_target; | |
| 650 PhiInstr* phi = use->instruction()->AsPhi(); | |
| 651 if (phi != NULL) { | |
| 652 ASSERT(phi->is_alive()); | |
| 653 // For phis conversions have to be inserted in the predecessor. | |
| 654 insert_before = | |
| 655 phi->block()->PredecessorAt(use->use_index())->last_instruction(); | |
| 656 deopt_target = NULL; | |
| 657 } else { | |
| 658 deopt_target = insert_before = use->instruction(); | |
| 659 } | |
| 660 | |
| 661 Definition* converted = NULL; | |
| 662 if (IsUnboxedInteger(from) && IsUnboxedInteger(to)) { | |
| 663 const intptr_t deopt_id = (to == kUnboxedInt32) && (deopt_target != NULL) ? | |
| 664 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; | |
| 665 converted = new(Z) UnboxedIntConverterInstr(from, | |
| 666 to, | |
| 667 use->CopyWithType(), | |
| 668 deopt_id); | |
| 669 } else if ((from == kUnboxedInt32) && (to == kUnboxedDouble)) { | |
| 670 converted = new Int32ToDoubleInstr(use->CopyWithType()); | |
| 671 } else if ((from == kUnboxedMint) && | |
| 672 (to == kUnboxedDouble) && | |
| 673 CanConvertUnboxedMintToDouble()) { | |
| 674 const intptr_t deopt_id = (deopt_target != NULL) ? | |
| 675 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; | |
| 676 ASSERT(CanUnboxDouble()); | |
| 677 converted = new MintToDoubleInstr(use->CopyWithType(), deopt_id); | |
| 678 } else if ((from == kTagged) && Boxing::Supports(to)) { | |
| 679 const intptr_t deopt_id = (deopt_target != NULL) ? | |
| 680 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; | |
| 681 converted = UnboxInstr::Create(to, use->CopyWithType(), deopt_id); | |
| 682 } else if ((to == kTagged) && Boxing::Supports(from)) { | |
| 683 converted = BoxInstr::Create(from, use->CopyWithType()); | |
| 684 } else { | |
| 685 // We have failed to find a suitable conversion instruction. | |
| 686 // Insert two "dummy" conversion instructions with the correct | |
| 687 // "from" and "to" representation. The inserted instructions will | |
| 688 // trigger a deoptimization if executed. See #12417 for a discussion. | |
| 689 const intptr_t deopt_id = (deopt_target != NULL) ? | |
| 690 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; | |
| 691 ASSERT(Boxing::Supports(from)); | |
| 692 ASSERT(Boxing::Supports(to)); | |
| 693 Definition* boxed = BoxInstr::Create(from, use->CopyWithType()); | |
| 694 use->BindTo(boxed); | |
| 695 InsertBefore(insert_before, boxed, NULL, FlowGraph::kValue); | |
| 696 converted = UnboxInstr::Create(to, new(Z) Value(boxed), deopt_id); | |
| 697 } | |
| 698 ASSERT(converted != NULL); | |
| 699 InsertBefore(insert_before, converted, use->instruction()->env(), | |
| 700 FlowGraph::kValue); | |
| 701 if (is_environment_use) { | |
| 702 use->BindToEnvironment(converted); | |
| 703 } else { | |
| 704 use->BindTo(converted); | |
| 705 } | |
| 706 | |
| 707 if ((to == kUnboxedInt32) && (phi != NULL)) { | |
| 708 // Int32 phis are unboxed optimistically. Ensure that unboxing | |
| 709 // has deoptimization target attached from the goto instruction. | |
| 710 flow_graph_->CopyDeoptTarget(converted, insert_before); | |
| 711 } | |
| 712 } | |
| 713 | |
| 714 | |
| 715 void FlowGraphOptimizer::ConvertUse(Value* use, Representation from_rep) { | |
| 716 const Representation to_rep = | |
| 717 use->instruction()->RequiredInputRepresentation(use->use_index()); | |
| 718 if (from_rep == to_rep || to_rep == kNoRepresentation) { | |
| 719 return; | |
| 720 } | |
| 721 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ false); | |
| 722 } | |
| 723 | |
| 724 | |
| 725 void FlowGraphOptimizer::ConvertEnvironmentUse(Value* use, | |
| 726 Representation from_rep) { | |
| 727 const Representation to_rep = kTagged; | |
| 728 if (from_rep == to_rep) { | |
| 729 return; | |
| 730 } | |
| 731 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ true); | |
| 732 } | |
| 733 | |
| 734 | |
| 735 void FlowGraphOptimizer::InsertConversionsFor(Definition* def) { | |
| 736 const Representation from_rep = def->representation(); | |
| 737 | |
| 738 for (Value::Iterator it(def->input_use_list()); | |
| 739 !it.Done(); | |
| 740 it.Advance()) { | |
| 741 ConvertUse(it.Current(), from_rep); | |
| 742 } | |
| 743 | |
| 744 if (flow_graph()->graph_entry()->SuccessorCount() > 1) { | |
| 745 for (Value::Iterator it(def->env_use_list()); | |
| 746 !it.Done(); | |
| 747 it.Advance()) { | |
| 748 Value* use = it.Current(); | |
| 749 if (use->instruction()->MayThrow() && | |
| 750 use->instruction()->GetBlock()->InsideTryBlock()) { | |
| 751 // Environment uses at calls inside try-blocks must be converted to | |
| 752 // tagged representation. | |
| 753 ConvertEnvironmentUse(it.Current(), from_rep); | |
| 754 } | |
| 755 } | |
| 756 } | |
| 757 } | |
| 758 | |
| 759 | |
| 760 static void UnboxPhi(PhiInstr* phi) { | |
| 761 Representation unboxed = phi->representation(); | |
| 762 | |
| 763 switch (phi->Type()->ToCid()) { | |
| 764 case kDoubleCid: | |
| 765 if (CanUnboxDouble()) { | |
| 766 unboxed = kUnboxedDouble; | |
| 767 } | |
| 768 break; | |
| 769 case kFloat32x4Cid: | |
| 770 if (ShouldInlineSimd()) { | |
| 771 unboxed = kUnboxedFloat32x4; | |
| 772 } | |
| 773 break; | |
| 774 case kInt32x4Cid: | |
| 775 if (ShouldInlineSimd()) { | |
| 776 unboxed = kUnboxedInt32x4; | |
| 777 } | |
| 778 break; | |
| 779 case kFloat64x2Cid: | |
| 780 if (ShouldInlineSimd()) { | |
| 781 unboxed = kUnboxedFloat64x2; | |
| 782 } | |
| 783 break; | |
| 784 } | |
| 785 | |
| 786 if ((kSmiBits < 32) && | |
| 787 (unboxed == kTagged) && | |
| 788 phi->Type()->IsInt() && | |
| 789 RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt64)) { | |
| 790 // On 32-bit platforms conservatively unbox phis that: | |
| 791 // - are proven to be of type Int; | |
| 792 // - fit into 64bits range; | |
| 793 // - have either constants or Box() operations as inputs; | |
| 794 // - have at least one Box() operation as an input; | |
| 795 // - are used in at least 1 Unbox() operation. | |
| 796 bool should_unbox = false; | |
| 797 for (intptr_t i = 0; i < phi->InputCount(); i++) { | |
| 798 Definition* input = phi->InputAt(i)->definition(); | |
| 799 if (input->IsBox() && | |
| 800 RangeUtils::Fits(input->range(), | |
| 801 RangeBoundary::kRangeBoundaryInt64)) { | |
| 802 should_unbox = true; | |
| 803 } else if (!input->IsConstant()) { | |
| 804 should_unbox = false; | |
| 805 break; | |
| 806 } | |
| 807 } | |
| 808 | |
| 809 if (should_unbox) { | |
| 810 // We checked inputs. Check if phi is used in at least one unbox | |
| 811 // operation. | |
| 812 bool has_unboxed_use = false; | |
| 813 for (Value* use = phi->input_use_list(); | |
| 814 use != NULL; | |
| 815 use = use->next_use()) { | |
| 816 Instruction* instr = use->instruction(); | |
| 817 if (instr->IsUnbox()) { | |
| 818 has_unboxed_use = true; | |
| 819 break; | |
| 820 } else if (IsUnboxedInteger( | |
| 821 instr->RequiredInputRepresentation(use->use_index()))) { | |
| 822 has_unboxed_use = true; | |
| 823 break; | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 if (!has_unboxed_use) { | |
| 828 should_unbox = false; | |
| 829 } | |
| 830 } | |
| 831 | |
| 832 if (should_unbox) { | |
| 833 unboxed = | |
| 834 RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32) | |
| 835 ? kUnboxedInt32 : kUnboxedMint; | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 phi->set_representation(unboxed); | |
| 840 } | |
| 841 | |
| 842 | |
| 843 void FlowGraphOptimizer::SelectRepresentations() { | |
| 844 // Conservatively unbox all phis that were proven to be of Double, | |
| 845 // Float32x4, or Int32x4 type. | |
| 846 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 847 !block_it.Done(); | |
| 848 block_it.Advance()) { | |
| 849 JoinEntryInstr* join_entry = block_it.Current()->AsJoinEntry(); | |
| 850 if (join_entry != NULL) { | |
| 851 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) { | |
| 852 PhiInstr* phi = it.Current(); | |
| 853 UnboxPhi(phi); | |
| 854 } | |
| 855 } | |
| 856 } | |
| 857 | |
| 858 // Process all instructions and insert conversions where needed. | |
| 859 GraphEntryInstr* graph_entry = flow_graph_->graph_entry(); | |
| 860 | |
| 861 // Visit incoming parameters and constants. | |
| 862 for (intptr_t i = 0; i < graph_entry->initial_definitions()->length(); i++) { | |
| 863 InsertConversionsFor((*graph_entry->initial_definitions())[i]); | |
| 864 } | |
| 865 | |
| 866 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 867 !block_it.Done(); | |
| 868 block_it.Advance()) { | |
| 869 BlockEntryInstr* entry = block_it.Current(); | |
| 870 JoinEntryInstr* join_entry = entry->AsJoinEntry(); | |
| 871 if (join_entry != NULL) { | |
| 872 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) { | |
| 873 PhiInstr* phi = it.Current(); | |
| 874 ASSERT(phi != NULL); | |
| 875 ASSERT(phi->is_alive()); | |
| 876 InsertConversionsFor(phi); | |
| 877 } | |
| 878 } | |
| 879 CatchBlockEntryInstr* catch_entry = entry->AsCatchBlockEntry(); | |
| 880 if (catch_entry != NULL) { | |
| 881 for (intptr_t i = 0; | |
| 882 i < catch_entry->initial_definitions()->length(); | |
| 883 i++) { | |
| 884 InsertConversionsFor((*catch_entry->initial_definitions())[i]); | |
| 885 } | |
| 886 } | |
| 887 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) { | |
| 888 Definition* def = it.Current()->AsDefinition(); | |
| 889 if (def != NULL) { | |
| 890 InsertConversionsFor(def); | |
| 891 } | |
| 892 } | |
| 893 } | |
| 894 } | |
| 895 | |
| 896 | |
| 897 static bool ClassIdIsOneOf(intptr_t class_id, | 588 static bool ClassIdIsOneOf(intptr_t class_id, |
| 898 const GrowableArray<intptr_t>& class_ids) { | 589 const GrowableArray<intptr_t>& class_ids) { |
| 899 for (intptr_t i = 0; i < class_ids.length(); i++) { | 590 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 900 ASSERT(class_ids[i] != kIllegalCid); | 591 ASSERT(class_ids[i] != kIllegalCid); |
| 901 if (class_ids[i] == class_id) { | 592 if (class_ids[i] == class_id) { |
| 902 return true; | 593 return true; |
| 903 } | 594 } |
| 904 } | 595 } |
| 905 return false; | 596 return false; |
| 906 } | 597 } |
| (...skipping 2807 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3714 } | 3405 } |
| 3715 | 3406 |
| 3716 // Discard the environment from the original instruction because the store | 3407 // Discard the environment from the original instruction because the store |
| 3717 // can't deoptimize. | 3408 // can't deoptimize. |
| 3718 instr->RemoveEnvironment(); | 3409 instr->RemoveEnvironment(); |
| 3719 ReplaceCall(instr, store); | 3410 ReplaceCall(instr, store); |
| 3720 return true; | 3411 return true; |
| 3721 } | 3412 } |
| 3722 | 3413 |
| 3723 | 3414 |
| 3724 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32) | |
| 3725 // Smi widening pass is only meaningful on platforms where Smi | |
| 3726 // is smaller than 32bit. For now only support it on ARM and ia32. | |
| 3727 static bool CanBeWidened(BinarySmiOpInstr* smi_op) { | |
| 3728 return BinaryInt32OpInstr::IsSupported(smi_op->op_kind(), | |
| 3729 smi_op->left(), | |
| 3730 smi_op->right()); | |
| 3731 } | |
| 3732 | |
| 3733 | |
| 3734 static bool BenefitsFromWidening(BinarySmiOpInstr* smi_op) { | |
| 3735 // TODO(vegorov): when shifts with non-constants shift count are supported | |
| 3736 // add them here as we save untagging for the count. | |
| 3737 switch (smi_op->op_kind()) { | |
| 3738 case Token::kMUL: | |
| 3739 case Token::kSHR: | |
| 3740 // For kMUL we save untagging of the argument for kSHR | |
| 3741 // we save tagging of the result. | |
| 3742 return true; | |
| 3743 | |
| 3744 default: | |
| 3745 return false; | |
| 3746 } | |
| 3747 } | |
| 3748 | |
| 3749 | |
| 3750 void FlowGraphOptimizer::WidenSmiToInt32() { | |
| 3751 GrowableArray<BinarySmiOpInstr*> candidates; | |
| 3752 | |
| 3753 // Step 1. Collect all instructions that potentially benefit from widening of | |
| 3754 // their operands (or their result) into int32 range. | |
| 3755 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 3756 !block_it.Done(); | |
| 3757 block_it.Advance()) { | |
| 3758 for (ForwardInstructionIterator instr_it(block_it.Current()); | |
| 3759 !instr_it.Done(); | |
| 3760 instr_it.Advance()) { | |
| 3761 BinarySmiOpInstr* smi_op = instr_it.Current()->AsBinarySmiOp(); | |
| 3762 if ((smi_op != NULL) && | |
| 3763 smi_op->HasSSATemp() && | |
| 3764 BenefitsFromWidening(smi_op) && | |
| 3765 CanBeWidened(smi_op)) { | |
| 3766 candidates.Add(smi_op); | |
| 3767 } | |
| 3768 } | |
| 3769 } | |
| 3770 | |
| 3771 if (candidates.is_empty()) { | |
| 3772 return; | |
| 3773 } | |
| 3774 | |
| 3775 // Step 2. For each block in the graph compute which loop it belongs to. | |
| 3776 // We will use this information later during computation of the widening's | |
| 3777 // gain: we are going to assume that only conversion occuring inside the | |
| 3778 // same loop should be counted against the gain, all other conversions | |
| 3779 // can be hoisted and thus cost nothing compared to the loop cost itself. | |
| 3780 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers = | |
| 3781 flow_graph()->LoopHeaders(); | |
| 3782 | |
| 3783 GrowableArray<intptr_t> loops(flow_graph_->preorder().length()); | |
| 3784 for (intptr_t i = 0; i < flow_graph_->preorder().length(); i++) { | |
| 3785 loops.Add(-1); | |
| 3786 } | |
| 3787 | |
| 3788 for (intptr_t loop_id = 0; loop_id < loop_headers.length(); ++loop_id) { | |
| 3789 for (BitVector::Iterator loop_it(loop_headers[loop_id]->loop_info()); | |
| 3790 !loop_it.Done(); | |
| 3791 loop_it.Advance()) { | |
| 3792 loops[loop_it.Current()] = loop_id; | |
| 3793 } | |
| 3794 } | |
| 3795 | |
| 3796 // Step 3. For each candidate transitively collect all other BinarySmiOpInstr | |
| 3797 // and PhiInstr that depend on it and that it depends on and count amount of | |
| 3798 // untagging operations that we save in assumption that this whole graph of | |
| 3799 // values is using kUnboxedInt32 representation instead of kTagged. | |
| 3800 // Convert those graphs that have positive gain to kUnboxedInt32. | |
| 3801 | |
| 3802 // BitVector containing SSA indexes of all processed definitions. Used to skip | |
| 3803 // those candidates that belong to dependency graph of another candidate. | |
| 3804 BitVector* processed = | |
| 3805 new(Z) BitVector(Z, flow_graph_->current_ssa_temp_index()); | |
| 3806 | |
| 3807 // Worklist used to collect dependency graph. | |
| 3808 DefinitionWorklist worklist(flow_graph_, candidates.length()); | |
| 3809 for (intptr_t i = 0; i < candidates.length(); i++) { | |
| 3810 BinarySmiOpInstr* op = candidates[i]; | |
| 3811 if (op->WasEliminated() || processed->Contains(op->ssa_temp_index())) { | |
| 3812 continue; | |
| 3813 } | |
| 3814 | |
| 3815 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3816 THR_Print("analysing candidate: %s\n", op->ToCString()); | |
| 3817 } | |
| 3818 worklist.Clear(); | |
| 3819 worklist.Add(op); | |
| 3820 | |
| 3821 // Collect dependency graph. Note: more items are added to worklist | |
| 3822 // inside this loop. | |
| 3823 intptr_t gain = 0; | |
| 3824 for (intptr_t j = 0; j < worklist.definitions().length(); j++) { | |
| 3825 Definition* defn = worklist.definitions()[j]; | |
| 3826 | |
| 3827 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3828 THR_Print("> %s\n", defn->ToCString()); | |
| 3829 } | |
| 3830 | |
| 3831 if (defn->IsBinarySmiOp() && | |
| 3832 BenefitsFromWidening(defn->AsBinarySmiOp())) { | |
| 3833 gain++; | |
| 3834 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3835 THR_Print("^ [%" Pd "] (o) %s\n", gain, defn->ToCString()); | |
| 3836 } | |
| 3837 } | |
| 3838 | |
| 3839 const intptr_t defn_loop = loops[defn->GetBlock()->preorder_number()]; | |
| 3840 | |
| 3841 // Process all inputs. | |
| 3842 for (intptr_t k = 0; k < defn->InputCount(); k++) { | |
| 3843 Definition* input = defn->InputAt(k)->definition(); | |
| 3844 if (input->IsBinarySmiOp() && | |
| 3845 CanBeWidened(input->AsBinarySmiOp())) { | |
| 3846 worklist.Add(input); | |
| 3847 } else if (input->IsPhi() && (input->Type()->ToCid() == kSmiCid)) { | |
| 3848 worklist.Add(input); | |
| 3849 } else if (input->IsBinaryMintOp()) { | |
| 3850 // Mint operation produces untagged result. We avoid tagging. | |
| 3851 gain++; | |
| 3852 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3853 THR_Print("^ [%" Pd "] (i) %s\n", gain, input->ToCString()); | |
| 3854 } | |
| 3855 } else if (defn_loop == loops[input->GetBlock()->preorder_number()] && | |
| 3856 (input->Type()->ToCid() == kSmiCid)) { | |
| 3857 // Input comes from the same loop, is known to be smi and requires | |
| 3858 // untagging. | |
| 3859 // TODO(vegorov) this heuristic assumes that values that are not | |
| 3860 // known to be smi have to be checked and this check can be | |
| 3861 // coalesced with untagging. Start coalescing them. | |
| 3862 gain--; | |
| 3863 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3864 THR_Print("v [%" Pd "] (i) %s\n", gain, input->ToCString()); | |
| 3865 } | |
| 3866 } | |
| 3867 } | |
| 3868 | |
| 3869 // Process all uses. | |
| 3870 for (Value* use = defn->input_use_list(); | |
| 3871 use != NULL; | |
| 3872 use = use->next_use()) { | |
| 3873 Instruction* instr = use->instruction(); | |
| 3874 Definition* use_defn = instr->AsDefinition(); | |
| 3875 if (use_defn == NULL) { | |
| 3876 // We assume that tagging before returning or pushing argument costs | |
| 3877 // very little compared to the cost of the return/call itself. | |
| 3878 if (!instr->IsReturn() && !instr->IsPushArgument()) { | |
| 3879 gain--; | |
| 3880 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3881 THR_Print("v [%" Pd "] (u) %s\n", | |
| 3882 gain, | |
| 3883 use->instruction()->ToCString()); | |
| 3884 } | |
| 3885 } | |
| 3886 continue; | |
| 3887 } else if (use_defn->IsBinarySmiOp() && | |
| 3888 CanBeWidened(use_defn->AsBinarySmiOp())) { | |
| 3889 worklist.Add(use_defn); | |
| 3890 } else if (use_defn->IsPhi() && | |
| 3891 use_defn->AsPhi()->Type()->ToCid() == kSmiCid) { | |
| 3892 worklist.Add(use_defn); | |
| 3893 } else if (use_defn->IsBinaryMintOp()) { | |
| 3894 // BinaryMintOp requires untagging of its inputs. | |
| 3895 // Converting kUnboxedInt32 to kUnboxedMint is essentially zero cost | |
| 3896 // sign extension operation. | |
| 3897 gain++; | |
| 3898 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3899 THR_Print("^ [%" Pd "] (u) %s\n", | |
| 3900 gain, | |
| 3901 use->instruction()->ToCString()); | |
| 3902 } | |
| 3903 } else if (defn_loop == loops[instr->GetBlock()->preorder_number()]) { | |
| 3904 gain--; | |
| 3905 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3906 THR_Print("v [%" Pd "] (u) %s\n", | |
| 3907 gain, | |
| 3908 use->instruction()->ToCString()); | |
| 3909 } | |
| 3910 } | |
| 3911 } | |
| 3912 } | |
| 3913 | |
| 3914 processed->AddAll(worklist.contains_vector()); | |
| 3915 | |
| 3916 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { | |
| 3917 THR_Print("~ %s gain %" Pd "\n", op->ToCString(), gain); | |
| 3918 } | |
| 3919 | |
| 3920 if (gain > 0) { | |
| 3921 // We have positive gain from widening. Convert all BinarySmiOpInstr into | |
| 3922 // BinaryInt32OpInstr and set representation of all phis to kUnboxedInt32. | |
| 3923 for (intptr_t j = 0; j < worklist.definitions().length(); j++) { | |
| 3924 Definition* defn = worklist.definitions()[j]; | |
| 3925 ASSERT(defn->IsPhi() || defn->IsBinarySmiOp()); | |
| 3926 | |
| 3927 if (defn->IsBinarySmiOp()) { | |
| 3928 BinarySmiOpInstr* smi_op = defn->AsBinarySmiOp(); | |
| 3929 BinaryInt32OpInstr* int32_op = new(Z) BinaryInt32OpInstr( | |
| 3930 smi_op->op_kind(), | |
| 3931 smi_op->left()->CopyWithType(), | |
| 3932 smi_op->right()->CopyWithType(), | |
| 3933 smi_op->DeoptimizationTarget()); | |
| 3934 | |
| 3935 smi_op->ReplaceWith(int32_op, NULL); | |
| 3936 } else if (defn->IsPhi()) { | |
| 3937 defn->AsPhi()->set_representation(kUnboxedInt32); | |
| 3938 ASSERT(defn->Type()->IsInt()); | |
| 3939 } | |
| 3940 } | |
| 3941 } | |
| 3942 } | |
| 3943 } | |
| 3944 #else | |
| 3945 void FlowGraphOptimizer::WidenSmiToInt32() { | |
| 3946 // TODO(vegorov) ideally on 64-bit platforms we would like to narrow smi | |
| 3947 // operations to 32-bit where it saves tagging and untagging and allows | |
| 3948 // to use shorted (and faster) instructions. But we currently don't | |
| 3949 // save enough range information in the ICData to drive this decision. | |
| 3950 } | |
| 3951 #endif | |
| 3952 | |
| 3953 | |
| 3954 void FlowGraphOptimizer::EliminateEnvironments() { | |
| 3955 // After this pass we can no longer perform LICM and hoist instructions | |
| 3956 // that can deoptimize. | |
| 3957 | |
| 3958 flow_graph_->disallow_licm(); | |
| 3959 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 3960 !block_it.Done(); | |
| 3961 block_it.Advance()) { | |
| 3962 BlockEntryInstr* block = block_it.Current(); | |
| 3963 block->RemoveEnvironment(); | |
| 3964 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | |
| 3965 Instruction* current = it.Current(); | |
| 3966 if (!current->CanDeoptimize()) { | |
| 3967 // TODO(srdjan): --source-lines needs deopt environments to get at | |
| 3968 // the code for this instruction, however, leaving the environment | |
| 3969 // changes code. | |
| 3970 current->RemoveEnvironment(); | |
| 3971 } | |
| 3972 } | |
| 3973 } | |
| 3974 } | |
| 3975 | |
| 3976 | |
| 3977 } // namespace dart | 3415 } // namespace dart |
| OLD | NEW |