Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart

Issue 23669006: Re-apply: "Fix http://code.google.com/p/dart/issues/detail?id=13007 by introducing a new Marker valu (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/compiler/dart2js/value_range_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/value_range_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698