| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library dart2js.cps_ir.bounds_checker; | 5 library dart2js.cps_ir.bounds_checker; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import 'optimizers.dart' show Pass; | 8 import 'optimizers.dart' show Pass; |
| 9 import 'octagon.dart'; | 9 import 'octagon.dart'; |
| 10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 11 import 'cps_fragment.dart'; | 11 import 'cps_fragment.dart'; |
| 12 import 'type_mask_system.dart'; | 12 import 'type_mask_system.dart'; |
| 13 import '../types/types.dart'; |
| 13 import '../world.dart'; | 14 import '../world.dart'; |
| 14 import '../elements/elements.dart'; | 15 import '../elements/elements.dart'; |
| 15 import 'loop_effects.dart'; | 16 import 'loop_effects.dart'; |
| 16 | 17 |
| 17 | |
| 18 | |
| 19 /// Eliminates bounds checks when they can be proven safe. | 18 /// Eliminates bounds checks when they can be proven safe. |
| 20 /// | 19 /// |
| 21 /// In general, this pass will try to eliminate any branch with arithmetic | 20 /// In general, this pass will try to eliminate any branch with arithmetic |
| 22 /// in the condition, i.e. `x < y`, `x <= y`, `x == y` etc. | 21 /// in the condition, i.e. `x < y`, `x <= y`, `x == y` etc. |
| 23 /// | 22 /// |
| 24 /// The analysis uses an [Octagon] abstract domain. Unlike traditional octagon | 23 /// The analysis uses an [Octagon] abstract domain. Unlike traditional octagon |
| 25 /// analyzers, we do not use a closed matrix representation, but just maintain | 24 /// analyzers, we do not use a closed matrix representation, but just maintain |
| 26 /// a bucket of constraints. Constraints can therefore be added and removed | 25 /// a bucket of constraints. Constraints can therefore be added and removed |
| 27 /// on-the-fly without significant overhead. | 26 /// on-the-fly without significant overhead. |
| 28 /// | 27 /// |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 if (isUInt32(number)) { | 106 if (isUInt32(number)) { |
| 108 min = 0; | 107 min = 0; |
| 109 max = MAX_UINT32; | 108 max = MAX_UINT32; |
| 110 } else if (isNonNegativeInt(number)) { | 109 } else if (isNonNegativeInt(number)) { |
| 111 min = 0; | 110 min = 0; |
| 112 } | 111 } |
| 113 return valueOf.putIfAbsent(number, () => octagon.makeVariable(min, max)); | 112 return valueOf.putIfAbsent(number, () => octagon.makeVariable(min, max)); |
| 114 } | 113 } |
| 115 | 114 |
| 116 /// Get a constraint variable representing the length of [indexableObject] at | 115 /// Get a constraint variable representing the length of [indexableObject] at |
| 117 /// program locations with the given [effectCounter]. | 116 /// program locations with the given [effectNumber]. |
| 118 SignedVariable getLength(Primitive indexableObject, int effectCounter) { | 117 SignedVariable getLength(Primitive indexableObject, int effectNumber) { |
| 119 indexableObject = indexableObject.effectiveDefinition; | 118 indexableObject = indexableObject.effectiveDefinition; |
| 120 if (indexableObject.type != null && | 119 TypeMask type = indexableObject.type.nonNullable(); |
| 121 types.isDefinitelyFixedLengthIndexable(indexableObject.type)) { | 120 if (types.isDefinitelyFixedLengthIndexable(type)) { |
| 122 // Always use the same effect counter if the length is immutable. | 121 // Always use the same effect number if the length is immutable. |
| 123 effectCounter = 0; | 122 effectNumber = 0; |
| 124 } | 123 } |
| 125 return lengthOf | 124 return lengthOf |
| 126 .putIfAbsent(indexableObject, () => <int, SignedVariable>{}) | 125 .putIfAbsent(indexableObject, () => <int, SignedVariable>{}) |
| 127 .putIfAbsent(effectCounter, () => octagon.makeVariable(0, MAX_UINT32)); | 126 .putIfAbsent(effectNumber, () { |
| 127 int length = types.getContainerLength(type); |
| 128 if (length != null) { |
| 129 return octagon.makeVariable(length, length); |
| 130 } else { |
| 131 return octagon.makeVariable(0, MAX_UINT32); |
| 132 } |
| 133 }); |
| 128 } | 134 } |
| 129 | 135 |
| 130 // ------------- CONSTRAINT HELPERS ----------------- | 136 // ------------- CONSTRAINT HELPERS ----------------- |
| 131 | 137 |
| 132 /// Puts the given constraint "in scope" by adding it to the octagon, and | 138 /// Puts the given constraint "in scope" by adding it to the octagon, and |
| 133 /// pushing a stack action that will remove it again. | 139 /// pushing a stack action that will remove it again. |
| 134 void applyConstraint(SignedVariable v1, SignedVariable v2, int k) { | 140 void applyConstraint(SignedVariable v1, SignedVariable v2, int k) { |
| 135 Constraint constraint = new Constraint(v1, v2, k); | 141 Constraint constraint = new Constraint(v1, v2, k); |
| 136 octagon.pushConstraint(constraint); | 142 octagon.pushConstraint(constraint); |
| 137 pushAction(() => octagon.popConstraint(constraint)); | 143 pushAction(() => octagon.popConstraint(constraint)); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 } else if (isDefinitelyGreaterThanOrEqualTo(v1, v2)) { | 237 } else if (isDefinitelyGreaterThanOrEqualTo(v1, v2)) { |
| 232 makeGreaterThan(v1, v2); | 238 makeGreaterThan(v1, v2); |
| 233 } | 239 } |
| 234 } | 240 } |
| 235 | 241 |
| 236 /// Return true if we can prove that `v1 <= v2`. | 242 /// Return true if we can prove that `v1 <= v2`. |
| 237 bool isDefinitelyLessThanOrEqualTo(SignedVariable v1, SignedVariable v2) { | 243 bool isDefinitelyLessThanOrEqualTo(SignedVariable v1, SignedVariable v2) { |
| 238 return testConstraint(v1, v2.negated, 0); | 244 return testConstraint(v1, v2.negated, 0); |
| 239 } | 245 } |
| 240 | 246 |
| 247 /// Return true if we can prove that `v1 < v2`. |
| 248 bool isDefinitelyLessThan(SignedVariable v1, SignedVariable v2) { |
| 249 return testConstraint(v1, v2.negated, -1); |
| 250 } |
| 251 |
| 241 /// Return true if we can prove that `v1 >= v2`. | 252 /// Return true if we can prove that `v1 >= v2`. |
| 242 bool isDefinitelyGreaterThanOrEqualTo(SignedVariable v1, SignedVariable v2) { | 253 bool isDefinitelyGreaterThanOrEqualTo(SignedVariable v1, SignedVariable v2) { |
| 243 return testConstraint(v2, v1.negated, 0); | 254 return testConstraint(v2, v1.negated, 0); |
| 244 } | 255 } |
| 245 | 256 |
| 246 bool isDefinitelyLessThanOrEqualToConstant(SignedVariable v1, int value) { | 257 bool isDefinitelyLessThanOrEqualToConstant(SignedVariable v1, int value) { |
| 247 // v1 <= value <==> v1 + v1 <= 2 * value | 258 // v1 <= value <==> v1 + v1 <= 2 * value |
| 248 return testConstraint(v1, v1, 2 * value); | 259 return testConstraint(v1, v1, 2 * value); |
| 249 } | 260 } |
| 250 | 261 |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 | 470 |
| 460 int getIntConstant(Primitive prim) { | 471 int getIntConstant(Primitive prim) { |
| 461 if (prim is Constant && prim.value.isInt) { | 472 if (prim is Constant && prim.value.isInt) { |
| 462 IntConstantValue constant = prim.value; | 473 IntConstantValue constant = prim.value; |
| 463 return constant.primitiveValue; | 474 return constant.primitiveValue; |
| 464 } | 475 } |
| 465 return null; | 476 return null; |
| 466 } | 477 } |
| 467 | 478 |
| 468 @override | 479 @override |
| 480 void visitRefinement(Refinement node) { |
| 481 // In general we should get the container length of the refined type and |
| 482 // add a constraint if we know the length after the refinement. |
| 483 // However, our current type system removes container information when a |
| 484 // type becomes part of a union, so this cannot happen. |
| 485 } |
| 486 |
| 487 @override |
| 469 void visitGetLength(GetLength node) { | 488 void visitGetLength(GetLength node) { |
| 470 valueOf[node] = getLength(node.object.definition, currentEffectNumber); | 489 valueOf[node] = getLength(node.object.definition, currentEffectNumber); |
| 471 } | 490 } |
| 472 | 491 |
| 492 @override |
| 493 void visitBoundsCheck(BoundsCheck node) { |
| 494 if (node.checks == BoundsCheck.NONE) return; |
| 495 assert(node.index != null); // Because there is at least one check. |
| 496 Primitive object = node.object.definition; |
| 497 SignedVariable length = node.length == null |
| 498 ? null |
| 499 : getValue(node.length.definition); |
| 500 SignedVariable index = getValue(node.index.definition); |
| 501 if (node.hasUpperBoundCheck) { |
| 502 if (isDefinitelyLessThan(index, length)) { |
| 503 node.checks &= ~BoundsCheck.UPPER_BOUND; |
| 504 } else { |
| 505 makeLessThan(index, length); |
| 506 } |
| 507 } |
| 508 if (node.hasLowerBoundCheck) { |
| 509 if (isDefinitelyGreaterThanOrEqualToConstant(index, 0)) { |
| 510 node.checks &= ~BoundsCheck.LOWER_BOUND; |
| 511 } else { |
| 512 makeGreaterThanOrEqualToConstant(index, 0); |
| 513 } |
| 514 } |
| 515 if (node.hasEmptinessCheck) { |
| 516 if (isDefinitelyGreaterThanOrEqualToConstant(length, 1)) { |
| 517 node.checks &= ~BoundsCheck.EMPTINESS; |
| 518 } else { |
| 519 makeGreaterThanOrEqualToConstant(length, 1); |
| 520 } |
| 521 } |
| 522 if (!node.lengthUsedInCheck && node.length != null) { |
| 523 node..length.unlink()..length = null; |
| 524 } |
| 525 if (node.checks == BoundsCheck.NONE) { |
| 526 // We can't remove the bounds check node because it may still be used to |
| 527 // restrict code motion. But the index is no longer needed. |
| 528 node..index.unlink()..index = null; |
| 529 } |
| 530 } |
| 531 |
| 473 void analyzeLoopEntry(InvokeContinuation node) { | 532 void analyzeLoopEntry(InvokeContinuation node) { |
| 474 foundLoop = true; | 533 foundLoop = true; |
| 475 Continuation cont = node.continuation.definition; | 534 Continuation cont = node.continuation.definition; |
| 476 if (isStrongLoopPass) { | 535 if (isStrongLoopPass) { |
| 477 for (int i = 0; i < node.arguments.length; ++i) { | 536 for (int i = 0; i < node.arguments.length; ++i) { |
| 478 Parameter param = cont.parameters[i]; | 537 Parameter param = cont.parameters[i]; |
| 479 if (!isInt(param)) continue; | 538 if (!isInt(param)) continue; |
| 480 Primitive initialValue = node.arguments[i].definition; | 539 Primitive initialValue = node.arguments[i].definition; |
| 481 SignedVariable initialVariable = getValue(initialValue); | 540 SignedVariable initialVariable = getValue(initialValue); |
| 482 Monotonicity mono = monotonicity[param]; | 541 Monotonicity mono = monotonicity[param]; |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 } | 732 } |
| 674 return node.body; | 733 return node.body; |
| 675 } | 734 } |
| 676 } | 735 } |
| 677 | 736 |
| 678 /// Lattice representing the known (weak) monotonicity of a loop variable. | 737 /// Lattice representing the known (weak) monotonicity of a loop variable. |
| 679 /// | 738 /// |
| 680 /// The lattice bottom is represented by `null` and represents the case where | 739 /// The lattice bottom is represented by `null` and represents the case where |
| 681 /// the loop variable never changes value during the loop. | 740 /// the loop variable never changes value during the loop. |
| 682 enum Monotonicity { NotMonotone, Increasing, Decreasing, } | 741 enum Monotonicity { NotMonotone, Increasing, Decreasing, } |
| OLD | NEW |