Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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_range_analysis.h" | 5 #include "vm/flow_graph_range_analysis.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/il_printer.h" | 8 #include "vm/il_printer.h" |
| 9 | 9 |
| 10 namespace dart { | 10 namespace dart { |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 238 if (IsIntegerDefinition(current)) { | 238 if (IsIntegerDefinition(current)) { |
| 239 values_.Add(current); | 239 values_.Add(current); |
| 240 } | 240 } |
| 241 } | 241 } |
| 242 } | 242 } |
| 243 | 243 |
| 244 JoinEntryInstr* join = block->AsJoinEntry(); | 244 JoinEntryInstr* join = block->AsJoinEntry(); |
| 245 if (join != NULL) { | 245 if (join != NULL) { |
| 246 for (PhiIterator phi_it(join); !phi_it.Done(); phi_it.Advance()) { | 246 for (PhiIterator phi_it(join); !phi_it.Done(); phi_it.Advance()) { |
| 247 PhiInstr* current = phi_it.Current(); | 247 PhiInstr* current = phi_it.Current(); |
| 248 if ((current->Type()->ToCid() == kSmiCid) || | 248 if (current->Type()->IsInt()) { |
| 249 (current->representation() == kUnboxedInt32)) { | |
| 250 values_.Add(current); | 249 values_.Add(current); |
| 251 } | 250 } |
| 252 } | 251 } |
| 253 } | 252 } |
| 254 | 253 |
| 255 for (ForwardInstructionIterator instr_it(block); | 254 for (ForwardInstructionIterator instr_it(block); |
| 256 !instr_it.Done(); | 255 !instr_it.Done(); |
| 257 instr_it.Advance()) { | 256 instr_it.Advance()) { |
| 258 Instruction* current = instr_it.Current(); | 257 Instruction* current = instr_it.Current(); |
| 259 Definition* defn = current->AsDefinition(); | 258 Definition* defn = current->AsDefinition(); |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 510 // and will have a range assigned to it. | 509 // and will have a range assigned to it. |
| 511 // Note: that we can't return NULL here because it is used as lattice's | 510 // Note: that we can't return NULL here because it is used as lattice's |
| 512 // bottom element to indicate that the range was not computed *yet*. | 511 // bottom element to indicate that the range was not computed *yet*. |
| 513 return &smi_range_; | 512 return &smi_range_; |
| 514 } | 513 } |
| 515 | 514 |
| 516 return range; | 515 return range; |
| 517 } | 516 } |
| 518 | 517 |
| 519 | 518 |
| 519 const Range* RangeAnalysis::GetIntRange(Value* value) const { | |
| 520 Definition* defn = value->definition(); | |
| 521 const Range* range = defn->range(); | |
| 522 | |
| 523 if ((range == NULL) && !defn->Type()->IsInt()) { | |
| 524 // Type propagator determined that reaching type for this use is int. | |
| 525 // However the definition itself is not a int-definition and | |
| 526 // thus it will never have range assigned to it. Just return the widest | |
| 527 // range possible for this value. | |
| 528 // It is safe to return Int64 range as this is the widest possible range | |
| 529 // supported by our unboxing operations - if this definition produces | |
| 530 // Bigint outside of Int64 we will deoptimize whenever we actually try | |
| 531 // to unbox it. | |
| 532 // Note: that we can't return NULL here because it is used as lattice's | |
| 533 // bottom element to indicate that the range was not computed *yet*. | |
| 534 return &int64_range_; | |
| 535 } | |
| 536 | |
| 537 return range; | |
| 538 } | |
| 539 | |
| 540 | |
| 520 static bool AreEqualDefinitions(Definition* a, Definition* b) { | 541 static bool AreEqualDefinitions(Definition* a, Definition* b) { |
| 521 a = UnwrapConstraint(a); | 542 a = UnwrapConstraint(a); |
| 522 b = UnwrapConstraint(b); | 543 b = UnwrapConstraint(b); |
| 523 return (a == b) || | 544 return (a == b) || |
| 524 (a->AllowsCSE() && | 545 (a->AllowsCSE() && |
| 525 a->Dependencies().IsNone() && | 546 a->Dependencies().IsNone() && |
| 526 b->AllowsCSE() && | 547 b->AllowsCSE() && |
| 527 b->Dependencies().IsNone() && | 548 b->Dependencies().IsNone() && |
| 528 a->Equals(b)); | 549 a->Equals(b)); |
| 529 } | 550 } |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 632 switch (op) { | 653 switch (op) { |
| 633 case WIDEN: return 'W'; | 654 case WIDEN: return 'W'; |
| 634 case NARROW: return 'N'; | 655 case NARROW: return 'N'; |
| 635 case NONE: return 'I'; | 656 case NONE: return 'I'; |
| 636 } | 657 } |
| 637 UNREACHABLE(); | 658 UNREACHABLE(); |
| 638 return ' '; | 659 return ' '; |
| 639 } | 660 } |
| 640 | 661 |
| 641 | 662 |
| 663 static RangeBoundary::RangeSize RangeSizeForPhi(Definition* phi) { | |
| 664 ASSERT(phi->IsPhi()); | |
| 665 if (phi->Type()->ToCid() == kSmiCid) { | |
| 666 return RangeBoundary::kRangeBoundarySmi; | |
| 667 } else if (phi->representation() == kUnboxedInt32) { | |
| 668 return RangeBoundary::kRangeBoundaryInt32; | |
| 669 } else if (phi->Type()->IsInt()) { | |
| 670 return RangeBoundary::kRangeBoundaryInt64; | |
| 671 } else { | |
| 672 UNREACHABLE(); | |
| 673 return RangeBoundary::kRangeBoundaryInt64; | |
| 674 } | |
| 675 } | |
| 676 | |
| 677 | |
| 642 bool RangeAnalysis::InferRange(JoinOperator op, | 678 bool RangeAnalysis::InferRange(JoinOperator op, |
| 643 Definition* defn, | 679 Definition* defn, |
| 644 intptr_t iteration) { | 680 intptr_t iteration) { |
| 645 Range range; | 681 Range range; |
| 646 defn->InferRange(this, &range); | 682 defn->InferRange(this, &range); |
| 647 | 683 |
| 648 if (!Range::IsUnknown(&range)) { | 684 if (!Range::IsUnknown(&range)) { |
| 649 if (!Range::IsUnknown(defn->range()) && defn->IsPhi()) { | 685 if (!Range::IsUnknown(defn->range()) && defn->IsPhi()) { |
| 650 // TODO(vegorov): we are currently supporting only smi/int32 phis. | 686 const RangeBoundary::RangeSize size = RangeSizeForPhi(defn); |
| 651 ASSERT((defn->Type()->ToCid() == kSmiCid) || | |
| 652 (defn->representation() == kUnboxedInt32)); | |
| 653 const RangeBoundary::RangeSize size = (defn->Type()->ToCid() == kSmiCid) ? | |
| 654 RangeBoundary::kRangeBoundarySmi : RangeBoundary::kRangeBoundaryInt32; | |
| 655 if (op == WIDEN) { | 687 if (op == WIDEN) { |
| 656 range = Range(WidenMin(defn->range(), &range, size), | 688 range = Range(WidenMin(defn->range(), &range, size), |
| 657 WidenMax(defn->range(), &range, size)); | 689 WidenMax(defn->range(), &range, size)); |
| 658 } else if (op == NARROW) { | 690 } else if (op == NARROW) { |
| 659 range = Range(NarrowMin(defn->range(), &range, size), | 691 range = Range(NarrowMin(defn->range(), &range, size), |
| 660 NarrowMax(defn->range(), &range, size)); | 692 NarrowMax(defn->range(), &range, size)); |
| 661 } | 693 } |
| 662 } | 694 } |
| 663 | 695 |
| 664 if (!range.Equals(defn->range())) { | 696 if (!range.Equals(defn->range())) { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 748 Definition* definition = initial[i]; | 780 Definition* definition = initial[i]; |
| 749 if (set->Contains(definition->ssa_temp_index())) { | 781 if (set->Contains(definition->ssa_temp_index())) { |
| 750 definitions_.Add(definition); | 782 definitions_.Add(definition); |
| 751 } | 783 } |
| 752 } | 784 } |
| 753 CollectDefinitions(set); | 785 CollectDefinitions(set); |
| 754 | 786 |
| 755 // Perform an iteration of range inference just propagating ranges | 787 // Perform an iteration of range inference just propagating ranges |
| 756 // through the graph as-is without applying widening or narrowing. | 788 // through the graph as-is without applying widening or narrowing. |
| 757 // This helps to improve precision of initial bounds. | 789 // This helps to improve precision of initial bounds. |
| 758 Iterate(NONE, 1); | 790 Iterate(NONE, 2); |
|
Florian Schneider
2014/11/10 16:10:47
Maybe a comment why 2 iterations help.
| |
| 759 | 791 |
| 760 // Perform fix-point iteration of range inference applying widening | 792 // Perform fix-point iteration of range inference applying widening |
| 761 // operator to phis to ensure fast convergence. | 793 // operator to phis to ensure fast convergence. |
| 762 // Widening simply maps growing bounds to the respective range bound. | 794 // Widening simply maps growing bounds to the respective range bound. |
| 763 Iterate(WIDEN, kMaxInt32); | 795 Iterate(WIDEN, kMaxInt32); |
| 764 | 796 |
| 765 if (FLAG_trace_range_analysis) { | 797 if (FLAG_trace_range_analysis) { |
| 766 FlowGraphPrinter::PrintGraph("Range Analysis (WIDEN)", flow_graph_); | 798 FlowGraphPrinter::PrintGraph("Range Analysis (WIDEN)", flow_graph_); |
| 767 } | 799 } |
| 768 | 800 |
| (...skipping 1816 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2585 } | 2617 } |
| 2586 | 2618 |
| 2587 | 2619 |
| 2588 void Definition::InferRange(RangeAnalysis* analysis, Range* range) { | 2620 void Definition::InferRange(RangeAnalysis* analysis, Range* range) { |
| 2589 if (Type()->ToCid() == kSmiCid) { | 2621 if (Type()->ToCid() == kSmiCid) { |
| 2590 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); | 2622 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); |
| 2591 } else if (IsMintDefinition()) { | 2623 } else if (IsMintDefinition()) { |
| 2592 *range = Range::Full(RangeBoundary::kRangeBoundaryInt64); | 2624 *range = Range::Full(RangeBoundary::kRangeBoundaryInt64); |
| 2593 } else if (IsInt32Definition()) { | 2625 } else if (IsInt32Definition()) { |
| 2594 *range = Range::Full(RangeBoundary::kRangeBoundaryInt32); | 2626 *range = Range::Full(RangeBoundary::kRangeBoundaryInt32); |
| 2627 } else if (Type()->IsInt()) { | |
| 2628 *range = Range::Full(RangeBoundary::kRangeBoundaryInt64); | |
| 2595 } else { | 2629 } else { |
| 2596 // Only Smi and Mint supported. | 2630 // Only Smi and Mint supported. |
| 2597 UNREACHABLE(); | 2631 UNREACHABLE(); |
| 2598 } | 2632 } |
| 2599 } | 2633 } |
| 2600 | 2634 |
| 2601 | 2635 |
| 2602 static bool DependsOnSymbol(const RangeBoundary& a, Definition* symbol) { | 2636 static bool DependsOnSymbol(const RangeBoundary& a, Definition* symbol) { |
| 2603 return a.IsSymbol() && (UnwrapConstraint(a.symbol()) == symbol); | 2637 return a.IsSymbol() && (UnwrapConstraint(a.symbol()) == symbol); |
| 2604 } | 2638 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2685 Definition* unwrapped = UnwrapConstraint(a.symbol()); | 2719 Definition* unwrapped = UnwrapConstraint(a.symbol()); |
| 2686 if ((unwrapped != a.symbol()) && | 2720 if ((unwrapped != a.symbol()) && |
| 2687 DominatesPhi(unwrapped->GetBlock(), phi_block)) { | 2721 DominatesPhi(unwrapped->GetBlock(), phi_block)) { |
| 2688 return RangeBoundary::FromDefinition(unwrapped, a.offset()); | 2722 return RangeBoundary::FromDefinition(unwrapped, a.offset()); |
| 2689 } | 2723 } |
| 2690 | 2724 |
| 2691 return limit; | 2725 return limit; |
| 2692 } | 2726 } |
| 2693 | 2727 |
| 2694 | 2728 |
| 2729 static const Range* GetInputRange(RangeAnalysis* analysis, | |
| 2730 RangeBoundary::RangeSize size, | |
| 2731 Value* input) { | |
| 2732 switch (size) { | |
| 2733 case RangeBoundary::kRangeBoundarySmi: | |
| 2734 return analysis->GetSmiRange(input); | |
| 2735 case RangeBoundary::kRangeBoundaryInt32: | |
| 2736 return input->definition()->range(); | |
| 2737 case RangeBoundary::kRangeBoundaryInt64: | |
| 2738 return analysis->GetIntRange(input); | |
| 2739 default: | |
| 2740 UNREACHABLE(); | |
| 2741 return NULL; | |
| 2742 } | |
| 2743 } | |
| 2744 | |
| 2745 | |
| 2695 void PhiInstr::InferRange(RangeAnalysis* analysis, Range* range) { | 2746 void PhiInstr::InferRange(RangeAnalysis* analysis, Range* range) { |
| 2696 ASSERT((Type()->ToCid() == kSmiCid) || (representation() == kUnboxedInt32)); | 2747 const RangeBoundary::RangeSize size = RangeSizeForPhi(this); |
| 2697 const RangeBoundary::RangeSize size = (Type()->ToCid() == kSmiCid) ? | |
| 2698 RangeBoundary::kRangeBoundarySmi : RangeBoundary::kRangeBoundaryInt32; | |
| 2699 for (intptr_t i = 0; i < InputCount(); i++) { | 2748 for (intptr_t i = 0; i < InputCount(); i++) { |
| 2700 Value* input = InputAt(i); | 2749 Value* input = InputAt(i); |
| 2701 const Range* input_range = (size == RangeBoundary::kRangeBoundarySmi) ? | |
| 2702 analysis->GetSmiRange(input) : input->definition()->range(); | |
| 2703 Join(range, | 2750 Join(range, |
| 2704 input->definition(), input_range, size); | 2751 input->definition(), |
| 2752 GetInputRange(analysis, size, input), | |
| 2753 size); | |
| 2705 } | 2754 } |
| 2706 | 2755 |
| 2707 BlockEntryInstr* phi_block = GetBlock(); | 2756 BlockEntryInstr* phi_block = GetBlock(); |
| 2708 range->set_min(EnsureAcyclicSymbol( | 2757 range->set_min(EnsureAcyclicSymbol( |
| 2709 phi_block, range->min(), RangeBoundary::MinSmi())); | 2758 phi_block, range->min(), RangeBoundary::MinSmi())); |
| 2710 range->set_max(EnsureAcyclicSymbol( | 2759 range->set_max(EnsureAcyclicSymbol( |
| 2711 phi_block, range->max(), RangeBoundary::MaxSmi())); | 2760 phi_block, range->max(), RangeBoundary::MaxSmi())); |
| 2712 } | 2761 } |
| 2713 | 2762 |
| 2714 | 2763 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2788 break; | 2837 break; |
| 2789 case kTypedDataInt16ArrayCid: | 2838 case kTypedDataInt16ArrayCid: |
| 2790 *range = Range(RangeBoundary::FromConstant(-32768), | 2839 *range = Range(RangeBoundary::FromConstant(-32768), |
| 2791 RangeBoundary::FromConstant(32767)); | 2840 RangeBoundary::FromConstant(32767)); |
| 2792 break; | 2841 break; |
| 2793 case kTypedDataUint16ArrayCid: | 2842 case kTypedDataUint16ArrayCid: |
| 2794 *range = Range(RangeBoundary::FromConstant(0), | 2843 *range = Range(RangeBoundary::FromConstant(0), |
| 2795 RangeBoundary::FromConstant(65535)); | 2844 RangeBoundary::FromConstant(65535)); |
| 2796 break; | 2845 break; |
| 2797 case kTypedDataInt32ArrayCid: | 2846 case kTypedDataInt32ArrayCid: |
| 2798 if (Typed32BitIsSmi()) { | 2847 *range = Range(RangeBoundary::FromConstant(kMinInt32), |
|
Florian Schneider
2014/11/10 16:10:47
Is Typed32BitIsSmi() still used anywhere?
| |
| 2799 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); | 2848 RangeBoundary::FromConstant(kMaxInt32)); |
| 2800 } else { | |
| 2801 *range = Range(RangeBoundary::FromConstant(kMinInt32), | |
| 2802 RangeBoundary::FromConstant(kMaxInt32)); | |
| 2803 } | |
| 2804 break; | 2849 break; |
| 2805 case kTypedDataUint32ArrayCid: | 2850 case kTypedDataUint32ArrayCid: |
| 2806 if (Typed32BitIsSmi()) { | 2851 *range = Range(RangeBoundary::FromConstant(0), |
| 2807 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); | 2852 RangeBoundary::FromConstant(kMaxUint32)); |
| 2808 } else { | |
| 2809 *range = Range(RangeBoundary::FromConstant(0), | |
| 2810 RangeBoundary::FromConstant(kMaxUint32)); | |
| 2811 } | |
| 2812 break; | 2853 break; |
| 2813 case kOneByteStringCid: | 2854 case kOneByteStringCid: |
| 2814 *range = Range(RangeBoundary::FromConstant(0), | 2855 *range = Range(RangeBoundary::FromConstant(0), |
| 2815 RangeBoundary::FromConstant(0xFF)); | 2856 RangeBoundary::FromConstant(0xFF)); |
| 2816 break; | 2857 break; |
| 2817 case kTwoByteStringCid: | 2858 case kTwoByteStringCid: |
| 2818 *range = Range(RangeBoundary::FromConstant(0), | 2859 *range = Range(RangeBoundary::FromConstant(0), |
| 2819 RangeBoundary::FromConstant(0xFFFF)); | 2860 RangeBoundary::FromConstant(0xFFFF)); |
| 2820 break; | 2861 break; |
| 2821 default: | 2862 default: |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2908 | 2949 |
| 2909 void BoxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) { | 2950 void BoxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) { |
| 2910 const Range* value_range = value()->definition()->range(); | 2951 const Range* value_range = value()->definition()->range(); |
| 2911 if (!Range::IsUnknown(value_range)) { | 2952 if (!Range::IsUnknown(value_range)) { |
| 2912 *range = *value_range; | 2953 *range = *value_range; |
| 2913 } | 2954 } |
| 2914 } | 2955 } |
| 2915 | 2956 |
| 2916 | 2957 |
| 2917 void UnboxInt32Instr::InferRange(RangeAnalysis* analysis, Range* range) { | 2958 void UnboxInt32Instr::InferRange(RangeAnalysis* analysis, Range* range) { |
| 2918 if (value()->definition()->Type()->ToCid() == kSmiCid) { | 2959 if (value()->Type()->ToCid() == kSmiCid) { |
| 2919 const Range* value_range = analysis->GetSmiRange(value()); | 2960 const Range* value_range = analysis->GetSmiRange(value()); |
| 2920 if (!Range::IsUnknown(value_range)) { | 2961 if (!Range::IsUnknown(value_range)) { |
| 2921 *range = *value_range; | 2962 *range = *value_range; |
| 2922 } | 2963 } |
| 2923 } else if (value()->definition()->IsMintDefinition() || | 2964 } else if (RangeAnalysis::IsIntegerDefinition(value()->definition())) { |
| 2924 value()->definition()->IsInt32Definition()) { | 2965 const Range* value_range = analysis->GetIntRange(value()); |
| 2925 const Range* value_range = value()->definition()->range(); | |
| 2926 if (!Range::IsUnknown(value_range)) { | 2966 if (!Range::IsUnknown(value_range)) { |
| 2927 *range = *value_range; | 2967 *range = *value_range; |
| 2928 } | 2968 } |
| 2929 } else if (value()->Type()->ToCid() == kSmiCid) { | 2969 } else if (value()->Type()->ToCid() == kSmiCid) { |
| 2930 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); | 2970 *range = Range::Full(RangeBoundary::kRangeBoundarySmi); |
| 2931 } else { | 2971 } else { |
| 2932 *range = Range::Full(RangeBoundary::kRangeBoundaryInt32); | 2972 *range = Range::Full(RangeBoundary::kRangeBoundaryInt32); |
| 2933 } | 2973 } |
| 2934 } | 2974 } |
| 2935 | 2975 |
| 2936 | 2976 |
| 2977 void UnboxUint32Instr::InferRange(RangeAnalysis* analysis, Range* range) { | |
| 2978 const Range* value_range = NULL; | |
| 2979 | |
| 2980 if (value()->Type()->ToCid() == kSmiCid) { | |
| 2981 value_range = analysis->GetSmiRange(value()); | |
| 2982 } else if (RangeAnalysis::IsIntegerDefinition(value()->definition())) { | |
| 2983 value_range = analysis->GetIntRange(value()); | |
| 2984 } else { | |
| 2985 *range = Range(RangeBoundary::FromConstant(0), | |
| 2986 RangeBoundary::FromConstant(kMaxUint32)); | |
| 2987 return; | |
| 2988 } | |
| 2989 | |
| 2990 if (!Range::IsUnknown(value_range)) { | |
| 2991 if (value_range->IsPositive()) { | |
| 2992 *range = *value_range; | |
| 2993 } else { | |
| 2994 *range = Range(RangeBoundary::FromConstant(0), | |
| 2995 RangeBoundary::FromConstant(kMaxUint32)); | |
| 2996 } | |
| 2997 } | |
| 2998 } | |
| 2999 | |
| 3000 | |
| 2937 void UnboxInt64Instr::InferRange(RangeAnalysis* analysis, Range* range) { | 3001 void UnboxInt64Instr::InferRange(RangeAnalysis* analysis, Range* range) { |
| 2938 const Range* value_range = value()->definition()->range(); | 3002 const Range* value_range = value()->definition()->range(); |
| 2939 if (value_range != NULL) { | 3003 if (value_range != NULL) { |
| 2940 *range = *value_range; | 3004 *range = *value_range; |
| 2941 } else if (!value()->definition()->IsMintDefinition() && | 3005 } else if (!value()->definition()->IsMintDefinition() && |
| 2942 (value()->definition()->Type()->ToCid() != kSmiCid)) { | 3006 (value()->definition()->Type()->ToCid() != kSmiCid)) { |
| 2943 *range = Range::Full(RangeBoundary::kRangeBoundaryInt64); | 3007 *range = Range::Full(RangeBoundary::kRangeBoundaryInt64); |
| 2944 } | 3008 } |
| 2945 } | 3009 } |
| 2946 | 3010 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3027 } | 3091 } |
| 3028 } while (CanonicalizeMaxBoundary(&max) || | 3092 } while (CanonicalizeMaxBoundary(&max) || |
| 3029 CanonicalizeMinBoundary(&canonical_length)); | 3093 CanonicalizeMinBoundary(&canonical_length)); |
| 3030 | 3094 |
| 3031 // Failed to prove that maximum is bounded with array length. | 3095 // Failed to prove that maximum is bounded with array length. |
| 3032 return false; | 3096 return false; |
| 3033 } | 3097 } |
| 3034 | 3098 |
| 3035 | 3099 |
| 3036 } // namespace dart | 3100 } // namespace dart |
| OLD | NEW |