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