| 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 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 | 7 |
| 8 class ValueRangeInfo { | 8 class ValueRangeInfo { |
| 9 final ConstantSystem constantSystem; | 9 final ConstantSystem constantSystem; |
| 10 | 10 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 return new NegateValue(value, this); | 40 return new NegateValue(value, this); |
| 41 } | 41 } |
| 42 | 42 |
| 43 Range newUnboundRange() { | 43 Range newUnboundRange() { |
| 44 return new Range.unbound(this); | 44 return new Range.unbound(this); |
| 45 } | 45 } |
| 46 | 46 |
| 47 Range newNormalizedRange(Value low, Value up) { | 47 Range newNormalizedRange(Value low, Value up) { |
| 48 return new Range.normalize(low, up, this); | 48 return new Range.normalize(low, up, this); |
| 49 } | 49 } |
| 50 | |
| 51 Range newMarkerRange() { | |
| 52 return new Range(new MarkerValue(false, this), | |
| 53 new MarkerValue(true, this), | |
| 54 this); | |
| 55 } | |
| 56 } | 50 } |
| 57 | 51 |
| 58 /** | 52 /** |
| 59 * A [Value] represents both symbolic values like the value of a | 53 * A [Value] represents both symbolic values like the value of a |
| 60 * parameter, or the length of an array, and concrete values, like | 54 * parameter, or the length of an array, and concrete values, like |
| 61 * constants. | 55 * constants. |
| 62 */ | 56 */ |
| 63 abstract class Value { | 57 abstract class Value { |
| 64 final ValueRangeInfo info; | 58 final ValueRangeInfo info; |
| 65 const Value(this.info); | 59 const Value(this.info); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 88 if (value.isNegative) return other; | 82 if (value.isNegative) return other; |
| 89 return const UnknownValue(); | 83 return const UnknownValue(); |
| 90 } | 84 } |
| 91 | 85 |
| 92 bool get isNegative => false; | 86 bool get isNegative => false; |
| 93 bool get isPositive => false; | 87 bool get isPositive => false; |
| 94 bool get isZero => false; | 88 bool get isZero => false; |
| 95 } | 89 } |
| 96 | 90 |
| 97 /** | 91 /** |
| 98 * The [MarkerValue] class is used to recognize ranges of loop | |
| 99 * updates. | |
| 100 */ | |
| 101 class MarkerValue extends Value { | |
| 102 /// If [positive] is true (respectively false), the marker goes | |
| 103 /// to [MaxIntValue] (respectively [MinIntValue]) when being added | |
| 104 /// to a positive (respectively negative) value. | |
| 105 final bool positive; | |
| 106 | |
| 107 const MarkerValue(this.positive, info) : super(info); | |
| 108 | |
| 109 Value operator +(Value other) { | |
| 110 if (other.isPositive && positive) return const MaxIntValue(); | |
| 111 if (other.isNegative && !positive) return const MinIntValue(); | |
| 112 if (other is IntValue) return this; | |
| 113 return const UnknownValue(); | |
| 114 } | |
| 115 | |
| 116 Value operator -(Value other) { | |
| 117 if (other.isPositive && !positive) return const MinIntValue(); | |
| 118 if (other.isNegative && positive) return const MaxIntValue(); | |
| 119 if (other is IntValue) return this; | |
| 120 return const UnknownValue(); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * An [IntValue] contains a constant integer value. | 92 * An [IntValue] contains a constant integer value. |
| 126 */ | 93 */ |
| 127 class IntValue extends Value { | 94 class IntValue extends Value { |
| 128 final int value; | 95 final int value; |
| 129 | 96 |
| 130 const IntValue(this.value, info) : super(info); | 97 const IntValue(this.value, info) : super(info); |
| 131 | 98 |
| 132 Value operator +(other) { | 99 Value operator +(other) { |
| 133 if (other.isZero) return this; | 100 if (other.isZero) return this; |
| 134 if (other is !IntValue) return other + this; | 101 if (other is !IntValue) return other + this; |
| (...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 Range.normalize(Value low, Value up, info) : this( | 447 Range.normalize(Value low, Value up, info) : this( |
| 481 low == const UnknownValue() ? const MinIntValue() : low, | 448 low == const UnknownValue() ? const MinIntValue() : low, |
| 482 up == const UnknownValue() ? const MaxIntValue() : up, | 449 up == const UnknownValue() ? const MaxIntValue() : up, |
| 483 info); | 450 info); |
| 484 | 451 |
| 485 Range union(Range other) { | 452 Range union(Range other) { |
| 486 return info.newNormalizedRange( | 453 return info.newNormalizedRange( |
| 487 lower.min(other.lower), upper.max(other.upper)); | 454 lower.min(other.lower), upper.max(other.upper)); |
| 488 } | 455 } |
| 489 | 456 |
| 490 Range intersection(Range other) { | 457 intersection(Range other) { |
| 491 Value low = lower.max(other.lower); | 458 Value low = lower.max(other.lower); |
| 492 Value up = upper.min(other.upper); | 459 Value up = upper.min(other.upper); |
| 493 // If we could not compute max or min, pick a value in the two | 460 // If we could not compute max or min, pick a value in the two |
| 494 // ranges, with priority to [IntValue]s because they are simpler. | 461 // ranges, with priority to [IntValue]s because they are simpler. |
| 495 if (low == const UnknownValue()) { | 462 if (low == const UnknownValue()) { |
| 496 if (lower is IntValue) low = lower; | 463 if (lower is IntValue) low = lower; |
| 497 else if (other.lower is IntValue) low = other.lower; | 464 else if (other.lower is IntValue) low = other.lower; |
| 498 else low = lower; | 465 else low = lower; |
| 499 } | 466 } |
| 500 if (up == const UnknownValue()) { | 467 if (up == const UnknownValue()) { |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 } | 614 } |
| 648 | 615 |
| 649 Range visitPhi(HPhi phi) { | 616 Range visitPhi(HPhi phi) { |
| 650 if (!phi.isInteger()) return info.newUnboundRange(); | 617 if (!phi.isInteger()) return info.newUnboundRange(); |
| 651 // Some phases may replace instructions that change the inputs of | 618 // Some phases may replace instructions that change the inputs of |
| 652 // this phi. Only the [SsaTypesPropagation] phase will update the | 619 // this phi. Only the [SsaTypesPropagation] phase will update the |
| 653 // phi type. Play it safe by assuming the [SsaTypesPropagation] | 620 // phi type. Play it safe by assuming the [SsaTypesPropagation] |
| 654 // phase is not necessarily run before the [ValueRangeAnalyzer]. | 621 // phase is not necessarily run before the [ValueRangeAnalyzer]. |
| 655 if (phi.inputs.any((i) => !i.isInteger())) return info.newUnboundRange(); | 622 if (phi.inputs.any((i) => !i.isInteger())) return info.newUnboundRange(); |
| 656 if (phi.block.isLoopHeader()) { | 623 if (phi.block.isLoopHeader()) { |
| 657 Range range = new LoopUpdateRecognizer(ranges, info).run(phi); | 624 Range range = tryInferLoopPhiRange(phi); |
| 658 if (range == null) return info.newUnboundRange(); | 625 if (range == null) return info.newUnboundRange(); |
| 659 return range; | 626 return range; |
| 660 } | 627 } |
| 661 | 628 |
| 662 Range range = ranges[phi.inputs[0]]; | 629 Range range = ranges[phi.inputs[0]]; |
| 663 for (int i = 1; i < phi.inputs.length; i++) { | 630 for (int i = 1; i < phi.inputs.length; i++) { |
| 664 range = range.union(ranges[phi.inputs[i]]); | 631 range = range.union(ranges[phi.inputs[i]]); |
| 665 } | 632 } |
| 666 return range; | 633 return range; |
| 667 } | 634 } |
| 668 | 635 |
| 636 Range tryInferLoopPhiRange(HPhi phi) { |
| 637 HInstruction update = phi.inputs[1]; |
| 638 return update.accept(new LoopUpdateRecognizer(phi, ranges, info)); |
| 639 } |
| 640 |
| 669 Range visitConstant(HConstant constant) { | 641 Range visitConstant(HConstant constant) { |
| 670 if (!constant.isInteger()) return info.newUnboundRange(); | 642 if (!constant.isInteger()) return info.newUnboundRange(); |
| 671 IntConstant constantInt = constant.constant; | 643 IntConstant constantInt = constant.constant; |
| 672 Value value = info.newIntValue(constantInt.value); | 644 Value value = info.newIntValue(constantInt.value); |
| 673 return info.newNormalizedRange(value, value); | 645 return info.newNormalizedRange(value, value); |
| 674 } | 646 } |
| 675 | 647 |
| 676 Range visitFieldGet(HFieldGet fieldGet) { | 648 Range visitFieldGet(HFieldGet fieldGet) { |
| 677 if (!fieldGet.isInteger()) return info.newUnboundRange(); | 649 if (!fieldGet.isInteger()) return info.newUnboundRange(); |
| 678 if (!fieldGet.receiver.isIndexable(compiler)) { | 650 if (!fieldGet.receiver.isIndexable(compiler)) { |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 929 | 901 |
| 930 return info.newUnboundRange(); | 902 return info.newUnboundRange(); |
| 931 } | 903 } |
| 932 | 904 |
| 933 Range visitRangeConversion(HRangeConversion conversion) { | 905 Range visitRangeConversion(HRangeConversion conversion) { |
| 934 return ranges[conversion]; | 906 return ranges[conversion]; |
| 935 } | 907 } |
| 936 } | 908 } |
| 937 | 909 |
| 938 /** | 910 /** |
| 939 * Tries to find a range for the update instruction of a loop phi. | 911 * Recognizes a number of patterns in a loop update instruction and |
| 912 * tries to infer a range for the loop phi. |
| 940 */ | 913 */ |
| 941 class LoopUpdateRecognizer extends HBaseVisitor { | 914 class LoopUpdateRecognizer extends HBaseVisitor { |
| 915 final HPhi loopPhi; |
| 942 final Map<HInstruction, Range> ranges; | 916 final Map<HInstruction, Range> ranges; |
| 943 final ValueRangeInfo info; | 917 final ValueRangeInfo info; |
| 944 LoopUpdateRecognizer(this.ranges, this.info); | 918 LoopUpdateRecognizer(this.loopPhi, this.ranges, this.info); |
| 945 | 919 |
| 946 Range run(HPhi loopPhi) { | 920 Range visitAdd(HAdd operation) { |
| 947 // Create a marker range for the loop phi, so that if the update | 921 Range range = getRangeForRecognizableOperation(operation); |
| 948 // uses the loop phi, it has a range to use. | 922 if (range == null) return info.newUnboundRange(); |
| 949 ranges[loopPhi] = info.newMarkerRange(); | 923 Range initial = ranges[loopPhi.inputs[0]]; |
| 950 Range updateRange = visit(loopPhi.inputs[1]); | 924 if (range.isPositive) { |
| 951 ranges[loopPhi] = null; | 925 return info.newNormalizedRange(initial.lower, const MaxIntValue()); |
| 952 if (updateRange == null) return null; | 926 } else if (range.isNegative) { |
| 953 Range startRange = ranges[loopPhi.inputs[0]]; | 927 return info.newNormalizedRange(const MinIntValue(), initial.upper); |
| 954 Value low = updateRange.lower is MarkerValue | 928 } |
| 955 ? startRange.lower | 929 return info.newUnboundRange(); |
| 956 : updateRange.lower; | |
| 957 Value up = updateRange.upper is MarkerValue | |
| 958 ? startRange.upper | |
| 959 : updateRange.upper; | |
| 960 return info.newNormalizedRange(low, up); | |
| 961 } | 930 } |
| 962 | 931 |
| 963 Range visit(HInstruction instruction) { | 932 Range visitSubtract(HSubtract operation) { |
| 964 if (!instruction.isInteger()) return null; | 933 Range range = getRangeForRecognizableOperation(operation); |
| 965 if (ranges[instruction] != null) return ranges[instruction]; | 934 if (range == null) return info.newUnboundRange(); |
| 966 return instruction.accept(this); | 935 Range initial = ranges[loopPhi.inputs[0]]; |
| 936 if (range.isPositive) { |
| 937 return info.newNormalizedRange(const MinIntValue(), initial.upper); |
| 938 } else if (range.isNegative) { |
| 939 return info.newNormalizedRange(initial.lower, const MaxIntValue()); |
| 940 } |
| 941 return info.newUnboundRange(); |
| 967 } | 942 } |
| 968 | 943 |
| 969 Range visitPhi(HPhi phi) { | 944 Range visitPhi(HPhi phi) { |
| 970 // If the update of a loop phi involves another loop phi, we give | |
| 971 // up. | |
| 972 if (phi.block.isLoopHeader()) return null; | |
| 973 Range phiRange; | 945 Range phiRange; |
| 974 for (HInstruction input in phi.inputs) { | 946 for (HInstruction input in phi.inputs) { |
| 975 Range inputRange = visit(input); | 947 HInstruction instruction = unwrap(input); |
| 948 // If one of the inputs is the loop phi, then we're only |
| 949 // interested in the other inputs: a loop phi feeding itself means |
| 950 // it is not being updated. |
| 951 if (instruction == loopPhi) continue; |
| 952 |
| 953 // If another loop phi is involved, it's too complex to analyze. |
| 954 if (instruction is HPhi && instruction.block.isLoopHeader()) return null; |
| 955 |
| 956 Range inputRange = instruction.accept(this); |
| 976 if (inputRange == null) return null; | 957 if (inputRange == null) return null; |
| 977 if (phiRange == null) { | 958 if (phiRange == null) { |
| 978 phiRange = inputRange; | 959 phiRange = inputRange; |
| 979 } else { | 960 } else { |
| 980 phiRange = phiRange.union(inputRange); | 961 phiRange = phiRange.union(inputRange); |
| 981 } | 962 } |
| 982 } | 963 } |
| 983 return phiRange; | 964 return phiRange; |
| 984 } | 965 } |
| 985 | 966 |
| 986 Range visitCheck(HCheck instruction) { | 967 /** |
| 987 return visit(instruction.checkedInput); | 968 * If [operation] is recognizable, returns the inferred range. |
| 969 * Otherwise returns [null]. |
| 970 */ |
| 971 Range getRangeForRecognizableOperation(HBinaryArithmetic operation) { |
| 972 if (!operation.left.isInteger()) return null; |
| 973 if (!operation.right.isInteger()) return null; |
| 974 HInstruction left = unwrap(operation.left); |
| 975 HInstruction right = unwrap(operation.right); |
| 976 // We only recognize operations that operate on the loop phi. |
| 977 bool isLeftLoopPhi = (left == loopPhi); |
| 978 bool isRightLoopPhi = (right == loopPhi); |
| 979 if (!isLeftLoopPhi && !isRightLoopPhi) return null; |
| 980 |
| 981 var other = isLeftLoopPhi ? right : left; |
| 982 // If the analysis already computed range for the update, use it. |
| 983 if (ranges[other] != null) return ranges[other]; |
| 984 |
| 985 // We currently only handle constants in updates if the |
| 986 // update does not have a range. |
| 987 if (other.isConstant()) { |
| 988 Value value = info.newIntValue(other.constant.value); |
| 989 return info.newNormalizedRange(value, value); |
| 990 } |
| 991 return null; |
| 988 } | 992 } |
| 989 | 993 |
| 990 Range visitAdd(HAdd operation) { | 994 /** |
| 991 return handleBinaryOperation(operation); | 995 * [HCheck] instructions may check the loop phi. Since we only |
| 992 } | 996 * recognize updates on the loop phi, we must [unwrap] the [HCheck] |
| 993 | 997 * instruction to check if it references the loop phi. |
| 994 Range visitSubtract(HSubtract operation) { | 998 */ |
| 995 return handleBinaryOperation(operation); | 999 HInstruction unwrap(instruction) { |
| 996 } | 1000 if (instruction is HCheck) return unwrap(instruction.checkedInput); |
| 997 | 1001 // [HPhi] might have two different [HCheck] instructions as |
| 998 Range handleBinaryOperation(HBinaryArithmetic instruction) { | 1002 // inputs, checking the same instruction. |
| 999 Range leftRange = visit(instruction.left); | 1003 if (instruction is HPhi && !instruction.block.isLoopHeader()) { |
| 1000 Range rightRange = visit(instruction.right); | 1004 HInstruction result = unwrap(instruction.inputs[0]); |
| 1001 if (leftRange == null || rightRange == null) return null; | 1005 for (int i = 1; i < instruction.inputs.length; i++) { |
| 1002 BinaryOperation operation = instruction.operation(info.constantSystem); | 1006 if (result != unwrap(instruction.inputs[i])) return instruction; |
| 1003 return operation.apply(leftRange, rightRange); | 1007 } |
| 1008 return result; |
| 1009 } |
| 1010 return instruction; |
| 1004 } | 1011 } |
| 1005 } | 1012 } |
| OLD | NEW |