| Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| index 081f80c998d78192d18bb569ceab4434d4a6d56a..b5a36ec7fa8e05b66034508e0ea1798a2c65e986 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -689,6 +689,15 @@ class ConstantPropagationLattice {
|
| if (!value.isNullable) return value;
|
| return nonConstant(value.type.nonNullable());
|
| }
|
| +
|
| + /// If [value] is an integer constant, returns its value, otherwise `null`.
|
| + int intValue(AbstractConstantValue value) {
|
| + if (value.isConstant && value.constant.isInt) {
|
| + PrimitiveConstantValue constant = value.constant;
|
| + return constant.primitiveValue;
|
| + }
|
| + return null;
|
| + }
|
| }
|
|
|
| /**
|
| @@ -1247,7 +1256,8 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| if (lattice.isDefinitelyString(receiverValue) &&
|
| lattice.isDefinitelyInt(getValue(index))) {
|
| SourceInformation sourceInfo = node.sourceInformation;
|
| - CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo);
|
| + CpsFragment cps = new CpsFragment(node.sourceInformation);
|
| + receiver = makeBoundsCheck(cps, receiver, index);
|
| ApplyBuiltinOperator get =
|
| cps.applyBuiltin(BuiltinOperator.CharCodeAt,
|
| <Primitive>[receiver, index]);
|
| @@ -1303,28 +1313,18 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| ///
|
| /// Returns a CPS fragment whose context is the branch where no error
|
| /// was thrown.
|
| - CpsFragment makeBoundsCheck(Primitive list,
|
| - Primitive index,
|
| - SourceInformation sourceInfo) {
|
| - CpsFragment cps = new CpsFragment(sourceInfo);
|
| + Primitive makeBoundsCheck(CpsFragment cps,
|
| + Primitive list,
|
| + Primitive index,
|
| + [int checkKind = BoundsCheck.BOTH_BOUNDS]) {
|
| if (compiler.trustPrimitives) {
|
| - return cps;
|
| + return cps.letPrim(new BoundsCheck.noCheck(list, cps.sourceInformation));
|
| + } else {
|
| + GetLength length = cps.letPrim(new GetLength(list));
|
| + list = cps.refine(list, typeSystem.nonNullType);
|
| + return cps.letPrim(new BoundsCheck(list, index, length, checkKind,
|
| + cps.sourceInformation));
|
| }
|
| - Continuation fail = cps.letCont();
|
| - if (!typeSystem.isDefinitelyNonNegativeInt(index.type)) {
|
| - Primitive isTooSmall = cps.applyBuiltin(
|
| - BuiltinOperator.NumLt,
|
| - <Primitive>[index, cps.makeZero()]);
|
| - cps.ifTruthy(isTooSmall).invokeContinuation(fail);
|
| - }
|
| - Primitive isTooLarge = cps.applyBuiltin(
|
| - BuiltinOperator.NumGe,
|
| - <Primitive>[index, cps.letPrim(new GetLength(list))]);
|
| - cps.ifTruthy(isTooLarge).invokeContinuation(fail);
|
| - cps.insideContinuation(fail).invokeStaticThrower(
|
| - helpers.throwIndexOutOfRangeException,
|
| - <Primitive>[list, index]);
|
| - return cps;
|
| }
|
|
|
| /// Create a check that throws if the length of [list] is not equal to
|
| @@ -1364,7 +1364,8 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| case '[]':
|
| Primitive index = node.dartArgument(0);
|
| if (!lattice.isDefinitelyInt(getValue(index))) return null;
|
| - CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo);
|
| + CpsFragment cps = new CpsFragment(node.sourceInformation);
|
| + receiver = makeBoundsCheck(cps, receiver, index);
|
| GetIndex get = cps.letPrim(new GetIndex(receiver, index));
|
| node.replaceUsesWith(get);
|
| // TODO(asgerf): Make replaceUsesWith set the hint?
|
| @@ -1379,7 +1380,8 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| Primitive index = node.dartArgument(0);
|
| Primitive value = node.dartArgument(1);
|
| if (!lattice.isDefinitelyInt(getValue(index))) return null;
|
| - CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo);
|
| + CpsFragment cps = new CpsFragment(node.sourceInformation);
|
| + receiver = makeBoundsCheck(cps, receiver, index);
|
| cps.letPrim(new SetIndex(receiver, index, value));
|
| assert(node.hasNoUses);
|
| return cps;
|
| @@ -1431,14 +1433,8 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| }
|
| if (!isExtendable) return null;
|
| CpsFragment cps = new CpsFragment(sourceInfo);
|
| - Primitive length = cps.letPrim(new GetLength(list));
|
| - Primitive isEmpty = cps.applyBuiltin(
|
| - BuiltinOperator.StrictEq,
|
| - [length, cps.makeZero()]);
|
| - CpsFragment fail = cps.ifTruthy(isEmpty);
|
| - fail.invokeStaticThrower(
|
| - helpers.throwIndexOutOfRangeException,
|
| - [list, fail.makeConstant(new IntConstantValue(-1))]);
|
| + list = makeBoundsCheck(cps, list, cps.makeMinusOne(),
|
| + BoundsCheck.EMPTINESS);
|
| Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop,
|
| list,
|
| <Primitive>[]);
|
| @@ -1481,7 +1477,8 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| if (listValue.isNullable) return null;
|
| Primitive index = node.dartArgument(0);
|
| if (!lattice.isDefinitelyInt(getValue(index))) return null;
|
| - CpsFragment cps = makeBoundsCheck(list, index, sourceInfo);
|
| + CpsFragment cps = new CpsFragment(node.sourceInformation);
|
| + list = makeBoundsCheck(cps, list, index);
|
| GetIndex get = cps.letPrim(new GetIndex(list, index));
|
| get.hint = node.hint;
|
| node.replaceUsesWith(get);
|
| @@ -2398,6 +2395,40 @@ class TransformingVisitor extends DeepRecursiveVisitor {
|
| return true;
|
| }
|
|
|
| + visitBoundsCheck(BoundsCheck node) {
|
| + // Eliminate bounds checks using constant folding.
|
| + // The [BoundsChecker] pass does not try to eliminate checks that could be
|
| + // eliminated by constant folding.
|
| + if (node.hasNoChecks) return;
|
| + int index = lattice.intValue(getValue(node.index.definition));
|
| + int length = node.length == null
|
| + ? null
|
| + : lattice.intValue(getValue(node.length.definition));
|
| + if (index != null && length != null && index < length) {
|
| + node.checks &= ~BoundsCheck.UPPER_BOUND;
|
| + }
|
| + if (index != null && index >= 0) {
|
| + node.checks &= ~BoundsCheck.LOWER_BOUND;
|
| + }
|
| + if (length != null && length > 0) {
|
| + node.checks &= ~BoundsCheck.EMPTINESS;
|
| + }
|
| + if (!node.lengthUsedInCheck && node.length != null) {
|
| + node..length.unlink()..length = null;
|
| + }
|
| + if (node.checks == BoundsCheck.NONE) {
|
| + // We can't remove the bounds check node because it may still be used to
|
| + // restrict code motion. But the index is no longer needed.
|
| + // TODO(asgerf): Since this was eliminated by constant folding, it should
|
| + // be safe to remove because any path-sensitive information we relied
|
| + // upon to do this are expressed by other refinement nodes that also
|
| + // restrict code motion. However, if we want to run this pass after
|
| + // [BoundsChecker] that would not be safe any more, so for now we
|
| + // keep the node for forward compatibilty.
|
| + node..index.unlink()..index = null;
|
| + }
|
| + }
|
| +
|
| visitNullCheck(NullCheck node) {
|
| if (!getValue(node.value.definition).isNullable) {
|
| node.replaceUsesWith(node.value.definition);
|
| @@ -3194,6 +3225,11 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
|
|
| @override
|
| + void visitBoundsCheck(BoundsCheck node) {
|
| + setValue(node, getValue(node.object.definition));
|
| + }
|
| +
|
| + @override
|
| void visitNullCheck(NullCheck node) {
|
| setValue(node, lattice.nonNullable(getValue(node.value.definition)));
|
| }
|
|
|