| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library dart2js.cps_ir.type_propagation; | 4 library dart2js.cps_ir.type_propagation; |
| 5 | 5 |
| 6 import 'optimizers.dart'; | 6 import 'optimizers.dart'; |
| 7 | 7 |
| 8 import '../closure.dart' show | 8 import '../closure.dart' show |
| 9 ClosureClassElement, Identifiers; | 9 ClosureClassElement, Identifiers; |
| 10 import '../common/names.dart' show | 10 import '../common/names.dart' show |
| (...skipping 960 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 971 if (!node.selector.isCall || | 971 if (!node.selector.isCall || |
| 972 node.selector.positionalArgumentCount != 1 || | 972 node.selector.positionalArgumentCount != 1 || |
| 973 node.selector.namedArgumentCount != 0) { | 973 node.selector.namedArgumentCount != 0) { |
| 974 return false; | 974 return false; |
| 975 } | 975 } |
| 976 if (!isExtendable) return false; | 976 if (!isExtendable) return false; |
| 977 Primitive addedItem = getDartArgument(node, 0); | 977 Primitive addedItem = getDartArgument(node, 0); |
| 978 CpsFragment cps = new CpsFragment(sourceInfo); | 978 CpsFragment cps = new CpsFragment(sourceInfo); |
| 979 cps.invokeBuiltin(BuiltinMethod.Push, | 979 cps.invokeBuiltin(BuiltinMethod.Push, |
| 980 list, | 980 list, |
| 981 <Primitive>[addedItem], | 981 <Primitive>[addedItem]); |
| 982 receiverIsNotNull: listValue.isDefinitelyNotNull); | |
| 983 cps.invokeContinuation(cont, [cps.makeNull()]); | 982 cps.invokeContinuation(cont, [cps.makeNull()]); |
| 984 replaceSubtree(node, cps.result); | 983 replaceSubtree(node, cps.result); |
| 985 push(cps.result); | 984 push(cps.result); |
| 986 return true; | 985 return true; |
| 987 | 986 |
| 988 case 'removeLast': | 987 case 'removeLast': |
| 989 if (!node.selector.isCall || | 988 if (!node.selector.isCall || |
| 990 node.selector.argumentCount != 0) { | 989 node.selector.argumentCount != 0) { |
| 991 return false; | 990 return false; |
| 992 } | 991 } |
| 993 if (!isExtendable) return false; | 992 if (!isExtendable) return false; |
| 994 CpsFragment cps = new CpsFragment(sourceInfo); | 993 CpsFragment cps = new CpsFragment(sourceInfo); |
| 995 Primitive length = cps.letPrim(new GetLength(list)); | 994 Primitive length = cps.letPrim(new GetLength(list)); |
| 996 Primitive isEmpty = cps.applyBuiltin( | 995 Primitive isEmpty = cps.applyBuiltin( |
| 997 BuiltinOperator.StrictEq, | 996 BuiltinOperator.StrictEq, |
| 998 [length, cps.makeZero()]); | 997 [length, cps.makeZero()]); |
| 999 CpsFragment fail = cps.ifTruthy(isEmpty); | 998 CpsFragment fail = cps.ifTruthy(isEmpty); |
| 1000 fail.invokeStaticThrower( | 999 fail.invokeStaticThrower( |
| 1001 backend.getThrowIndexOutOfBoundsError(), | 1000 backend.getThrowIndexOutOfBoundsError(), |
| 1002 [list, fail.makeConstant(new IntConstantValue(-1))]); | 1001 [list, fail.makeConstant(new IntConstantValue(-1))]); |
| 1003 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, | 1002 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, |
| 1004 list, | 1003 list, |
| 1005 <Primitive>[], | 1004 <Primitive>[]); |
| 1006 receiverIsNotNull: listValue.isDefinitelyNotNull); | |
| 1007 cps.invokeContinuation(cont, [removedItem]); | 1005 cps.invokeContinuation(cont, [removedItem]); |
| 1008 replaceSubtree(node, cps.result); | 1006 replaceSubtree(node, cps.result); |
| 1009 push(cps.result); | 1007 push(cps.result); |
| 1010 return true; | 1008 return true; |
| 1011 | 1009 |
| 1012 case 'addAll': | 1010 case 'addAll': |
| 1013 if (!node.selector.isCall || | 1011 if (!node.selector.isCall || |
| 1014 node.selector.argumentCount != 1) { | 1012 node.selector.argumentCount != 1) { |
| 1015 return false; | 1013 return false; |
| 1016 } | 1014 } |
| 1017 if (!isExtendable) return false; | 1015 if (!isExtendable) return false; |
| 1018 Primitive addedList = getDartArgument(node, 0); | 1016 Primitive addedList = getDartArgument(node, 0); |
| 1019 // Rewrite addAll([x1, ..., xN]) to push(x1, ..., xN). | 1017 // Rewrite addAll([x1, ..., xN]) to push(x1, ..., xN). |
| 1020 // Ensure that the list is not mutated between creation and use. | 1018 // Ensure that the list is not mutated between creation and use. |
| 1021 // We aim for the common case where this is the only use of the list, | 1019 // We aim for the common case where this is the only use of the list, |
| 1022 // which also guarantees that this list is not mutated before use. | 1020 // which also guarantees that this list is not mutated before use. |
| 1023 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { | 1021 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
| 1024 return false; | 1022 return false; |
| 1025 } | 1023 } |
| 1026 LiteralList addedLiteral = addedList; | 1024 LiteralList addedLiteral = addedList; |
| 1027 CpsFragment cps = new CpsFragment(sourceInfo); | 1025 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1028 cps.invokeBuiltin(BuiltinMethod.Push, | 1026 cps.invokeBuiltin(BuiltinMethod.Push, |
| 1029 list, | 1027 list, |
| 1030 addedLiteral.values.map((ref) => ref.definition).toList(), | 1028 addedLiteral.values.map((ref) => ref.definition).toList()); |
| 1031 receiverIsNotNull: listValue.isDefinitelyNotNull); | |
| 1032 cps.invokeContinuation(cont, [cps.makeNull()]); | 1029 cps.invokeContinuation(cont, [cps.makeNull()]); |
| 1033 replaceSubtree(node, cps.result); | 1030 replaceSubtree(node, cps.result); |
| 1034 push(cps.result); | 1031 push(cps.result); |
| 1035 return true; | 1032 return true; |
| 1036 | 1033 |
| 1037 case 'elementAt': | 1034 case 'elementAt': |
| 1038 if (!node.selector.isCall || | 1035 if (!node.selector.isCall || |
| 1039 node.selector.positionalArgumentCount != 1 || | 1036 node.selector.positionalArgumentCount != 1 || |
| 1040 node.selector.namedArgumentCount != 0) { | 1037 node.selector.namedArgumentCount != 0) { |
| 1041 return false; | 1038 return false; |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1469 void visitInvokeMethod(InvokeMethod node) { | 1466 void visitInvokeMethod(InvokeMethod node) { |
| 1470 if (constifyExpression(node)) return; | 1467 if (constifyExpression(node)) return; |
| 1471 if (specializeOperatorCall(node)) return; | 1468 if (specializeOperatorCall(node)) return; |
| 1472 if (specializeFieldAccess(node)) return; | 1469 if (specializeFieldAccess(node)) return; |
| 1473 if (specializeIndexableAccess(node)) return; | 1470 if (specializeIndexableAccess(node)) return; |
| 1474 if (specializeArrayAccess(node)) return; | 1471 if (specializeArrayAccess(node)) return; |
| 1475 if (specializeSingleUseClosureCall(node)) return; | 1472 if (specializeSingleUseClosureCall(node)) return; |
| 1476 if (specializeClosureCall(node)) return; | 1473 if (specializeClosureCall(node)) return; |
| 1477 | 1474 |
| 1478 AbstractValue receiver = getValue(node.receiver.definition); | 1475 AbstractValue receiver = getValue(node.receiver.definition); |
| 1479 node.receiverIsNotNull = receiver.isDefinitelyNotNull; | |
| 1480 | 1476 |
| 1481 if (node.receiverIsIntercepted && | 1477 if (node.receiverIsIntercepted && |
| 1482 node.receiver.definition.sameValue(node.arguments[0].definition)) { | 1478 node.receiver.definition.sameValue(node.arguments[0].definition)) { |
| 1483 // The receiver and first argument are the same; that means we already | 1479 // The receiver and first argument are the same; that means we already |
| 1484 // determined in visitInterceptor that we are targeting a non-interceptor. | 1480 // determined in visitInterceptor that we are targeting a non-interceptor. |
| 1485 | 1481 |
| 1486 // Check if any of the possible targets depend on the extra receiver | 1482 // Check if any of the possible targets depend on the extra receiver |
| 1487 // argument. Mixins do this, and tear-offs always needs the extra receiver | 1483 // argument. Mixins do this, and tear-offs always needs the extra receiver |
| 1488 // argument because BoundClosure uses it for equality and hash code. | 1484 // argument because BoundClosure uses it for equality and hash code. |
| 1489 // TODO(15933): Make automatically generated property extraction | 1485 // TODO(15933): Make automatically generated property extraction |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1826 if (node.dartType == dartTypes.coreTypes.numType || | 1822 if (node.dartType == dartTypes.coreTypes.numType || |
| 1827 node.dartType == dartTypes.coreTypes.doubleType) { | 1823 node.dartType == dartTypes.coreTypes.doubleType) { |
| 1828 return new ApplyBuiltinOperator( | 1824 return new ApplyBuiltinOperator( |
| 1829 BuiltinOperator.IsNumber, | 1825 BuiltinOperator.IsNumber, |
| 1830 <Primitive>[prim], | 1826 <Primitive>[prim], |
| 1831 node.sourceInformation); | 1827 node.sourceInformation); |
| 1832 } | 1828 } |
| 1833 return null; | 1829 return null; |
| 1834 } | 1830 } |
| 1835 | 1831 |
| 1836 void visitGetField(GetField node) { | |
| 1837 node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull; | |
| 1838 } | |
| 1839 | |
| 1840 void visitGetLength(GetLength node) { | |
| 1841 node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull; | |
| 1842 } | |
| 1843 | |
| 1844 Primitive visitInterceptor(Interceptor node) { | 1832 Primitive visitInterceptor(Interceptor node) { |
| 1845 AbstractValue value = getValue(node.input.definition); | 1833 AbstractValue value = getValue(node.input.definition); |
| 1846 // If the exact class of the input is known, replace with a constant | 1834 // If the exact class of the input is known, replace with a constant |
| 1847 // or the input itself. | 1835 // or the input itself. |
| 1848 ClassElement singleClass; | 1836 ClassElement singleClass; |
| 1849 if (lattice.isDefinitelyInt(value)) { | 1837 if (lattice.isDefinitelyInt(value)) { |
| 1850 // Classes like JSUInt31 and JSUInt32 do not exist at runtime, so ensure | 1838 // Classes like JSUInt31 and JSUInt32 do not exist at runtime, so ensure |
| 1851 // all the int classes get mapped tor their runtime class. | 1839 // all the int classes get mapped tor their runtime class. |
| 1852 singleClass = backend.jsIntClass; | 1840 singleClass = backend.jsIntClass; |
| 1853 } else if (lattice.isDefinitelyNativeList(value)) { | 1841 } else if (lattice.isDefinitelyNativeList(value)) { |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2132 // continuation. Note that this is effectively a phi node in SSA terms. | 2120 // continuation. Note that this is effectively a phi node in SSA terms. |
| 2133 for (int i = 0; i < node.arguments.length; i++) { | 2121 for (int i = 0; i < node.arguments.length; i++) { |
| 2134 Primitive def = node.arguments[i].definition; | 2122 Primitive def = node.arguments[i].definition; |
| 2135 AbstractValue cell = getValue(def); | 2123 AbstractValue cell = getValue(def); |
| 2136 setValue(cont.parameters[i], cell); | 2124 setValue(cont.parameters[i], cell); |
| 2137 } | 2125 } |
| 2138 } | 2126 } |
| 2139 | 2127 |
| 2140 void visitInvokeMethod(InvokeMethod node) { | 2128 void visitInvokeMethod(InvokeMethod node) { |
| 2141 AbstractValue receiver = getValue(node.receiver.definition); | 2129 AbstractValue receiver = getValue(node.receiver.definition); |
| 2130 node.receiverIsNotNull = receiver.isDefinitelyNotNull; |
| 2142 if (receiver.isNothing) { | 2131 if (receiver.isNothing) { |
| 2143 return; // And come back later. | 2132 return; // And come back later. |
| 2144 } | 2133 } |
| 2145 if (!node.selector.isOperator) { | 2134 if (!node.selector.isOperator) { |
| 2146 // TODO(jgruber): Handle known methods on constants such as String.length. | 2135 // TODO(jgruber): Handle known methods on constants such as String.length. |
| 2147 setResult(node, lattice.getInvokeReturnType(node.selector, node.mask)); | 2136 setResult(node, lattice.getInvokeReturnType(node.selector, node.mask)); |
| 2148 return; | 2137 return; |
| 2149 } | 2138 } |
| 2150 | 2139 |
| 2151 // Calculate the resulting constant if possible. | 2140 // Calculate the resulting constant if possible. |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2436 // null gets returned. | 2425 // null gets returned. |
| 2437 // TODO(asgerf): Add the NullInterceptor when it enables us to | 2426 // TODO(asgerf): Add the NullInterceptor when it enables us to |
| 2438 // propagate an assignment. | 2427 // propagate an assignment. |
| 2439 setValue(node, nonConstant()); | 2428 setValue(node, nonConstant()); |
| 2440 } else { | 2429 } else { |
| 2441 setValue(node, nonConstant(typeSystem.nonNullType)); | 2430 setValue(node, nonConstant(typeSystem.nonNullType)); |
| 2442 } | 2431 } |
| 2443 } | 2432 } |
| 2444 | 2433 |
| 2445 void visitGetField(GetField node) { | 2434 void visitGetField(GetField node) { |
| 2435 node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull; |
| 2446 setValue(node, nonConstant(typeSystem.getFieldType(node.field))); | 2436 setValue(node, nonConstant(typeSystem.getFieldType(node.field))); |
| 2447 } | 2437 } |
| 2448 | 2438 |
| 2449 void visitSetField(SetField node) {} | 2439 void visitSetField(SetField node) {} |
| 2450 | 2440 |
| 2451 void visitCreateBox(CreateBox node) { | 2441 void visitCreateBox(CreateBox node) { |
| 2452 setValue(node, nonConstant(typeSystem.nonNullType)); | 2442 setValue(node, nonConstant(typeSystem.nonNullType)); |
| 2453 } | 2443 } |
| 2454 | 2444 |
| 2455 void visitCreateInstance(CreateInstance node) { | 2445 void visitCreateInstance(CreateInstance node) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2481 @override | 2471 @override |
| 2482 void visitForeignCode(ForeignCode node) { | 2472 void visitForeignCode(ForeignCode node) { |
| 2483 if (node.continuation != null) { | 2473 if (node.continuation != null) { |
| 2484 setResult(node, nonConstant(node.type)); | 2474 setResult(node, nonConstant(node.type)); |
| 2485 } | 2475 } |
| 2486 } | 2476 } |
| 2487 | 2477 |
| 2488 @override | 2478 @override |
| 2489 void visitGetLength(GetLength node) { | 2479 void visitGetLength(GetLength node) { |
| 2490 AbstractValue input = getValue(node.object.definition); | 2480 AbstractValue input = getValue(node.object.definition); |
| 2481 node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull; |
| 2491 int length = typeSystem.getContainerLength(input.type); | 2482 int length = typeSystem.getContainerLength(input.type); |
| 2492 if (length != null) { | 2483 if (length != null) { |
| 2493 // TODO(asgerf): Constant-folding the length might degrade the VM's | 2484 // TODO(asgerf): Constant-folding the length might degrade the VM's |
| 2494 // own bounds-check elimination? | 2485 // own bounds-check elimination? |
| 2495 setValue(node, constantValue(new IntConstantValue(length))); | 2486 setValue(node, constantValue(new IntConstantValue(length))); |
| 2496 } else { | 2487 } else { |
| 2497 setValue(node, nonConstant(typeSystem.intType)); | 2488 setValue(node, nonConstant(typeSystem.intType)); |
| 2498 } | 2489 } |
| 2499 } | 2490 } |
| 2500 | 2491 |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2641 processLetPrim(LetPrim node) { | 2632 processLetPrim(LetPrim node) { |
| 2642 node.primitive.type = null; | 2633 node.primitive.type = null; |
| 2643 values[node.primitive] = null; | 2634 values[node.primitive] = null; |
| 2644 } | 2635 } |
| 2645 | 2636 |
| 2646 processLetMutable(LetMutable node) { | 2637 processLetMutable(LetMutable node) { |
| 2647 node.variable.type = null; | 2638 node.variable.type = null; |
| 2648 values[node.variable] = null; | 2639 values[node.variable] = null; |
| 2649 } | 2640 } |
| 2650 } | 2641 } |
| OLD | NEW |