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

Side by Side Diff: lib/compiler/implementation/ssa/nodes.dart

Issue 11341041: Fix for dartbug.com/6036: the intersection of two different types is not always conflicting. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 1 month 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
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 abstract class HVisitor<R> { 7 abstract class HVisitor<R> {
8 R visitAdd(HAdd node); 8 R visitAdd(HAdd node);
9 R visitBailoutTarget(HBailoutTarget node); 9 R visitBailoutTarget(HBailoutTarget node);
10 R visitBitAnd(HBitAnd node); 10 R visitBitAnd(HBitAnd node);
(...skipping 881 matching lines...) Expand 10 before | Expand all | Expand 10 after
892 * than the provided type for [this]. 892 * than the provided type for [this].
893 * 893 *
894 * Examples: the likely type of [:x == y:] is a boolean. In most cases this 894 * Examples: the likely type of [:x == y:] is a boolean. In most cases this
895 * cannot be guaranteed, but when merging types we still want to use this 895 * cannot be guaranteed, but when merging types we still want to use this
896 * information. 896 * information.
897 * 897 *
898 * Similarily the [HAdd] instruction is likely a number. Note that, even if 898 * Similarily the [HAdd] instruction is likely a number. Note that, even if
899 * the incoming type is already set to integer, the likely type might still 899 * the incoming type is already set to integer, the likely type might still
900 * just return the number type. 900 * just return the number type.
901 */ 901 */
902 HType computeLikelyType(HTypeMap types) => types[this]; 902 HType computeLikelyType(HTypeMap types, Compiler compiler) => types[this];
903 903
904 /** 904 /**
905 * Compute the type of the instruction by propagating the input types through 905 * Compute the type of the instruction by propagating the input types through
906 * the instruction. 906 * the instruction.
907 * 907 *
908 * By default just copy the guaranteed type. 908 * By default just copy the guaranteed type.
909 */ 909 */
910 HType computeTypeFromInputTypes(HTypeMap types) => guaranteedType; 910 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
911 return guaranteedType;
912 }
911 913
912 /** 914 /**
913 * Compute the desired type for the the given [input]. Aside from using 915 * Compute the desired type for the the given [input]. Aside from using
914 * other inputs to compute the desired type one should also use 916 * other inputs to compute the desired type one should also use
915 * the given [types] which, during the invocation of this method, 917 * the given [types] which, during the invocation of this method,
916 * represents the desired type of [this]. 918 * represents the desired type of [this].
917 */ 919 */
918 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) { 920 HType computeDesiredTypeForInput(HInstruction input,
921 HTypeMap types,
922 Compiler compiler) {
919 return HType.UNKNOWN; 923 return HType.UNKNOWN;
920 } 924 }
921 925
922 bool isInBasicBlock() => block != null; 926 bool isInBasicBlock() => block != null;
923 927
924 String inputsToString() { 928 String inputsToString() {
925 void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) { 929 void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) {
926 for (int i = 0; i < list.length; i++) { 930 for (int i = 0; i < list.length; i++) {
927 if (i != 0) buffer.add(', '); 931 if (i != 0) buffer.add(', ');
928 buffer.add("@${list[i].id}"); 932 buffer.add("@${list[i].id}");
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
1202 bool isEnabled = false; 1206 bool isEnabled = false;
1203 1207
1204 HTypeGuard(this.guardedType, HInstruction guarded, HInstruction bailoutTarget) 1208 HTypeGuard(this.guardedType, HInstruction guarded, HInstruction bailoutTarget)
1205 : super(<HInstruction>[guarded, bailoutTarget]); 1209 : super(<HInstruction>[guarded, bailoutTarget]);
1206 1210
1207 HInstruction get guarded => inputs[0]; 1211 HInstruction get guarded => inputs[0];
1208 HInstruction get checkedInput => guarded; 1212 HInstruction get checkedInput => guarded;
1209 HBailoutTarget get bailoutTarget => inputs[1]; 1213 HBailoutTarget get bailoutTarget => inputs[1];
1210 int get state => bailoutTarget.state; 1214 int get state => bailoutTarget.state;
1211 1215
1212 HType computeTypeFromInputTypes(HTypeMap types) { 1216 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1213 return isEnabled ? guardedType : types[guarded]; 1217 return isEnabled ? guardedType : types[guarded];
1214 } 1218 }
1215 1219
1216 HType get guaranteedType => isEnabled ? guardedType : HType.UNKNOWN; 1220 HType get guaranteedType => isEnabled ? guardedType : HType.UNKNOWN;
1217 1221
1218 bool isControlFlow() => true; 1222 bool isControlFlow() => true;
1219 1223
1220 bool isJsStatement(HTypeMap types) => isEnabled; 1224 bool isJsStatement(HTypeMap types) => isEnabled;
1221 1225
1222 accept(HVisitor visitor) => visitor.visitTypeGuard(this); 1226 accept(HVisitor visitor) => visitor.visitTypeGuard(this);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1254 class HIntegerCheck extends HCheck { 1258 class HIntegerCheck extends HCheck {
1255 bool alwaysFalse = false; 1259 bool alwaysFalse = false;
1256 1260
1257 HIntegerCheck(value) : super(<HInstruction>[value]); 1261 HIntegerCheck(value) : super(<HInstruction>[value]);
1258 1262
1259 HInstruction get value => inputs[0]; 1263 HInstruction get value => inputs[0];
1260 bool isControlFlow() => true; 1264 bool isControlFlow() => true;
1261 1265
1262 HType get guaranteedType => HType.INTEGER; 1266 HType get guaranteedType => HType.INTEGER;
1263 1267
1264 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) { 1268 HType computeDesiredTypeForInput(HInstruction input,
1269 HTypeMap types,
1270 Compiler compiler) {
1265 // If the desired type of the input is already a number, we want 1271 // If the desired type of the input is already a number, we want
1266 // to specialize it to an integer. 1272 // to specialize it to an integer.
1267 return input.isNumber(types) 1273 return input.isNumber(types)
1268 ? HType.INTEGER 1274 ? HType.INTEGER
1269 : super.computeDesiredTypeForInput(input, types); 1275 : super.computeDesiredTypeForInput(input, types, compiler);
1270 } 1276 }
1271 1277
1272 accept(HVisitor visitor) => visitor.visitIntegerCheck(this); 1278 accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
1273 int typeCode() => HInstruction.INTEGER_CHECK_TYPECODE; 1279 int typeCode() => HInstruction.INTEGER_CHECK_TYPECODE;
1274 bool typeEquals(other) => other is HIntegerCheck; 1280 bool typeEquals(other) => other is HIntegerCheck;
1275 bool dataEquals(HInstruction other) => true; 1281 bool dataEquals(HInstruction other) => true;
1276 } 1282 }
1277 1283
1278 abstract class HConditionalBranch extends HControlFlow { 1284 abstract class HConditionalBranch extends HControlFlow {
1279 HConditionalBranch(inputs) : super(inputs); 1285 HConditionalBranch(inputs) : super(inputs);
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
1387 HInvokeStatic(inputs, [HType knownType = HType.UNKNOWN]) : super(inputs) { 1393 HInvokeStatic(inputs, [HType knownType = HType.UNKNOWN]) : super(inputs) {
1388 guaranteedType = knownType; 1394 guaranteedType = knownType;
1389 } 1395 }
1390 1396
1391 toString() => 'invoke static: ${element.name}'; 1397 toString() => 'invoke static: ${element.name}';
1392 accept(HVisitor visitor) => visitor.visitInvokeStatic(this); 1398 accept(HVisitor visitor) => visitor.visitInvokeStatic(this);
1393 int typeCode() => HInstruction.INVOKE_STATIC_TYPECODE; 1399 int typeCode() => HInstruction.INVOKE_STATIC_TYPECODE;
1394 Element get element => target.element; 1400 Element get element => target.element;
1395 HStatic get target => inputs[0]; 1401 HStatic get target => inputs[0];
1396 1402
1397 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) { 1403 HType computeDesiredTypeForInput(HInstruction input,
1404 HTypeMap types,
1405 Compiler compiler) {
1398 // TODO(floitsch): we want the target to be a function. 1406 // TODO(floitsch): we want the target to be a function.
1399 if (input == target) return HType.UNKNOWN; 1407 if (input == target) return HType.UNKNOWN;
1400 return computeDesiredTypeForNonTargetInput(input, types); 1408 return computeDesiredTypeForNonTargetInput(input, types, compiler);
1401 } 1409 }
1402 1410
1403 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1411 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1404 HTypeMap types) { 1412 HTypeMap types,
1413 Compiler compiler) {
1405 return HType.UNKNOWN; 1414 return HType.UNKNOWN;
1406 } 1415 }
1407 } 1416 }
1408 1417
1409 class HInvokeSuper extends HInvokeStatic { 1418 class HInvokeSuper extends HInvokeStatic {
1410 final bool isSetter; 1419 final bool isSetter;
1411 HInvokeSuper(inputs, {this.isSetter: false}) : super(inputs); 1420 HInvokeSuper(inputs, {this.isSetter: false}) : super(inputs);
1412 toString() => 'invoke super: ${element.name}'; 1421 toString() => 'invoke super: ${element.name}';
1413 accept(HVisitor visitor) => visitor.visitInvokeSuper(this); 1422 accept(HVisitor visitor) => visitor.visitInvokeSuper(this);
1414 1423
(...skipping 25 matching lines...) Expand all
1440 return selector.isCall() 1449 return selector.isCall()
1441 && inputs[1].isExtendableArray(types) 1450 && inputs[1].isExtendableArray(types)
1442 && selector.name == const SourceString('removeLast') 1451 && selector.name == const SourceString('removeLast')
1443 && selector.argumentCount == 0; 1452 && selector.argumentCount == 0;
1444 } 1453 }
1445 1454
1446 bool isLengthGetterOnStringOrArray(HTypeMap types) { 1455 bool isLengthGetterOnStringOrArray(HTypeMap types) {
1447 return isLengthGetter() && inputs[1].isIndexablePrimitive(types); 1456 return isLengthGetter() && inputs[1].isIndexablePrimitive(types);
1448 } 1457 }
1449 1458
1450 HType computeLikelyType(HTypeMap types) { 1459 HType computeLikelyType(HTypeMap types, Compiler compiler) {
1451 // In general a length getter or method returns an int. 1460 // In general a length getter or method returns an int.
1452 if (isLengthGetter()) return HType.INTEGER; 1461 if (isLengthGetter()) return HType.INTEGER;
1453 return HType.UNKNOWN; 1462 return HType.UNKNOWN;
1454 } 1463 }
1455 1464
1456 HType computeTypeFromInputTypes(HTypeMap types) { 1465 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1457 if (isLengthGetterOnStringOrArray(types)) return HType.INTEGER; 1466 if (isLengthGetterOnStringOrArray(types)) return HType.INTEGER;
1458 return HType.UNKNOWN; 1467 return HType.UNKNOWN;
1459 } 1468 }
1460 1469
1461 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1470 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1462 HTypeMap types) { 1471 HTypeMap types,
1472 Compiler compiler) {
1463 // If the first argument is a string or an array and we invoke methods 1473 // If the first argument is a string or an array and we invoke methods
1464 // on it that mutate it, then we want to restrict the incoming type to be 1474 // on it that mutate it, then we want to restrict the incoming type to be
1465 // a mutable array. 1475 // a mutable array.
1466 if (input == inputs[1] && input.isIndexablePrimitive(types)) { 1476 if (input == inputs[1] && input.isIndexablePrimitive(types)) {
1467 // TODO(kasperl): Should we check that the selector is a call selector? 1477 // TODO(kasperl): Should we check that the selector is a call selector?
1468 if (selector.name == const SourceString('add') 1478 if (selector.name == const SourceString('add')
1469 || selector.name == const SourceString('removeLast')) { 1479 || selector.name == const SourceString('removeLast')) {
1470 return HType.MUTABLE_ARRAY; 1480 return HType.MUTABLE_ARRAY;
1471 } 1481 }
1472 } 1482 }
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
1632 if (isBuiltin(types)) { 1642 if (isBuiltin(types)) {
1633 setUseGvn(); 1643 setUseGvn();
1634 } else { 1644 } else {
1635 setAllSideEffects(); 1645 setAllSideEffects();
1636 } 1646 }
1637 } 1647 }
1638 1648
1639 bool isBuiltin(HTypeMap types) 1649 bool isBuiltin(HTypeMap types)
1640 => left.isNumber(types) && right.isNumber(types); 1650 => left.isNumber(types) && right.isNumber(types);
1641 1651
1642 HType computeTypeFromInputTypes(HTypeMap types) { 1652 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1643 if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER; 1653 if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER;
1644 if (left.isNumber(types)) { 1654 if (left.isNumber(types)) {
1645 if (left.isDouble(types) || right.isDouble(types)) return HType.DOUBLE; 1655 if (left.isDouble(types) || right.isDouble(types)) return HType.DOUBLE;
1646 return HType.NUMBER; 1656 return HType.NUMBER;
1647 } 1657 }
1648 return HType.UNKNOWN; 1658 return HType.UNKNOWN;
1649 } 1659 }
1650 1660
1651 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1661 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1652 HTypeMap types) { 1662 HTypeMap types,
1663 Compiler compiler) {
1653 HType propagatedType = types[this]; 1664 HType propagatedType = types[this];
1654 // If the desired output type should be an integer we want to get two 1665 // If the desired output type should be an integer we want to get two
1655 // integers as arguments. 1666 // integers as arguments.
1656 if (propagatedType.isInteger()) return HType.INTEGER; 1667 if (propagatedType.isInteger()) return HType.INTEGER;
1657 // If the outgoing type should be a number we can get that if both inputs 1668 // If the outgoing type should be a number we can get that if both inputs
1658 // are numbers. If we don't know the outgoing type we try to make it a 1669 // are numbers. If we don't know the outgoing type we try to make it a
1659 // number. 1670 // number.
1660 if (propagatedType.isUnknown() || propagatedType.isNumber()) { 1671 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1661 return HType.NUMBER; 1672 return HType.NUMBER;
1662 } 1673 }
1663 // Even if the desired outgoing type is not a number we still want the 1674 // Even if the desired outgoing type is not a number we still want the
1664 // second argument to be a number if the first one is a number. This will 1675 // second argument to be a number if the first one is a number. This will
1665 // not help for the outgoing type, but at least the binary arithmetic 1676 // not help for the outgoing type, but at least the binary arithmetic
1666 // operation will not have type problems. 1677 // operation will not have type problems.
1667 // TODO(floitsch): normally we shouldn't request a number, but simply 1678 // TODO(floitsch): normally we shouldn't request a number, but simply
1668 // throw an ArgumentError if it isn't. This would be similar 1679 // throw an ArgumentError if it isn't. This would be similar
1669 // to the array case. 1680 // to the array case.
1670 if (input == right && left.isNumber(types)) return HType.NUMBER; 1681 if (input == right && left.isNumber(types)) return HType.NUMBER;
1671 return HType.UNKNOWN; 1682 return HType.UNKNOWN;
1672 } 1683 }
1673 1684
1674 HType computeLikelyType(HTypeMap types) { 1685 HType computeLikelyType(HTypeMap types, Compiler compiler) {
1675 if (left.isTypeUnknown(types)) return HType.NUMBER; 1686 if (left.isTypeUnknown(types)) return HType.NUMBER;
1676 return HType.UNKNOWN; 1687 return HType.UNKNOWN;
1677 } 1688 }
1678 1689
1679 abstract BinaryOperation operation(ConstantSystem constantSystem); 1690 abstract BinaryOperation operation(ConstantSystem constantSystem);
1680 } 1691 }
1681 1692
1682 class HAdd extends HBinaryArithmetic { 1693 class HAdd extends HBinaryArithmetic {
1683 HAdd(HStatic target, HInstruction left, HInstruction right) 1694 HAdd(HStatic target, HInstruction left, HInstruction right)
1684 : super(target, left, right); 1695 : super(target, left, right);
1685 accept(HVisitor visitor) => visitor.visitAdd(this); 1696 accept(HVisitor visitor) => visitor.visitAdd(this);
1686 1697
1687 BinaryOperation operation(ConstantSystem constantSystem) 1698 BinaryOperation operation(ConstantSystem constantSystem)
1688 => constantSystem.add; 1699 => constantSystem.add;
1689 int typeCode() => HInstruction.ADD_TYPECODE; 1700 int typeCode() => HInstruction.ADD_TYPECODE;
1690 bool typeEquals(other) => other is HAdd; 1701 bool typeEquals(other) => other is HAdd;
1691 bool dataEquals(HInstruction other) => true; 1702 bool dataEquals(HInstruction other) => true;
1692 } 1703 }
1693 1704
1694 class HDivide extends HBinaryArithmetic { 1705 class HDivide extends HBinaryArithmetic {
1695 HDivide(HStatic target, HInstruction left, HInstruction right) 1706 HDivide(HStatic target, HInstruction left, HInstruction right)
1696 : super(target, left, right); 1707 : super(target, left, right);
1697 accept(HVisitor visitor) => visitor.visitDivide(this); 1708 accept(HVisitor visitor) => visitor.visitDivide(this);
1698 1709
1699 HType computeTypeFromInputTypes(HTypeMap types) { 1710 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1700 if (left.isNumber(types)) return HType.DOUBLE; 1711 if (left.isNumber(types)) return HType.DOUBLE;
1701 return HType.UNKNOWN; 1712 return HType.UNKNOWN;
1702 } 1713 }
1703 1714
1704 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1715 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1705 HTypeMap types) { 1716 HTypeMap types,
1717 Compiler compiler) {
1706 // A division can never return an integer. So don't ask for integer inputs. 1718 // A division can never return an integer. So don't ask for integer inputs.
1707 if (isInteger(types)) return HType.UNKNOWN; 1719 if (isInteger(types)) return HType.UNKNOWN;
1708 return super.computeDesiredTypeForNonTargetInput(input, types); 1720 return super.computeDesiredTypeForNonTargetInput(input, types, compiler);
1709 } 1721 }
1710 1722
1711 BinaryOperation operation(ConstantSystem constantSystem) 1723 BinaryOperation operation(ConstantSystem constantSystem)
1712 => constantSystem.divide; 1724 => constantSystem.divide;
1713 int typeCode() => HInstruction.DIVIDE_TYPECODE; 1725 int typeCode() => HInstruction.DIVIDE_TYPECODE;
1714 bool typeEquals(other) => other is HDivide; 1726 bool typeEquals(other) => other is HDivide;
1715 bool dataEquals(HInstruction other) => true; 1727 bool dataEquals(HInstruction other) => true;
1716 } 1728 }
1717 1729
1718 class HModulo extends HBinaryArithmetic { 1730 class HModulo extends HBinaryArithmetic {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1786 bool dataEquals(HInstruction other) => true; 1798 bool dataEquals(HInstruction other) => true;
1787 } 1799 }
1788 1800
1789 1801
1790 // TODO(floitsch): Should HBinaryArithmetic really be the super class of 1802 // TODO(floitsch): Should HBinaryArithmetic really be the super class of
1791 // HBinaryBitOp? 1803 // HBinaryBitOp?
1792 abstract class HBinaryBitOp extends HBinaryArithmetic { 1804 abstract class HBinaryBitOp extends HBinaryArithmetic {
1793 HBinaryBitOp(HStatic target, HInstruction left, HInstruction right) 1805 HBinaryBitOp(HStatic target, HInstruction left, HInstruction right)
1794 : super(target, left, right); 1806 : super(target, left, right);
1795 1807
1796 HType computeTypeFromInputTypes(HTypeMap types) { 1808 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1797 // All bitwise operations on primitive types either produce an 1809 // All bitwise operations on primitive types either produce an
1798 // integer or throw an error. 1810 // integer or throw an error.
1799 if (left.isPrimitive(types)) return HType.INTEGER; 1811 if (left.isPrimitive(types)) return HType.INTEGER;
1800 return HType.UNKNOWN; 1812 return HType.UNKNOWN;
1801 } 1813 }
1802 1814
1803 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1815 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1804 HTypeMap types) { 1816 HTypeMap types,
1817 Compiler compiler) {
1805 HType propagatedType = types[this]; 1818 HType propagatedType = types[this];
1806 // If the outgoing type should be a number we can get that only if both 1819 // If the outgoing type should be a number we can get that only if both
1807 // inputs are integers. If we don't know the outgoing type we try to make 1820 // inputs are integers. If we don't know the outgoing type we try to make
1808 // it an integer. 1821 // it an integer.
1809 if (propagatedType.isUnknown() || propagatedType.isNumber()) { 1822 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1810 return HType.INTEGER; 1823 return HType.INTEGER;
1811 } 1824 }
1812 return HType.UNKNOWN; 1825 return HType.UNKNOWN;
1813 } 1826 }
1814 1827
1815 HType computeLikelyType(HTypeMap types) { 1828 HType computeLikelyType(HTypeMap types, Compiler compiler) {
1816 if (left.isTypeUnknown(types)) return HType.INTEGER; 1829 if (left.isTypeUnknown(types)) return HType.INTEGER;
1817 return HType.UNKNOWN; 1830 return HType.UNKNOWN;
1818 } 1831 }
1819 1832
1820 // TODO(floitsch): make class abstract instead of adding an abstract method. 1833 // TODO(floitsch): make class abstract instead of adding an abstract method.
1821 abstract accept(HVisitor visitor); 1834 abstract accept(HVisitor visitor);
1822 } 1835 }
1823 1836
1824 class HShiftLeft extends HBinaryBitOp { 1837 class HShiftLeft extends HBinaryBitOp {
1825 HShiftLeft(HStatic target, HInstruction left, HInstruction right) 1838 HShiftLeft(HStatic target, HInstruction left, HInstruction right)
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
1907 // number. 1920 // number.
1908 if (isBuiltin(types)) { 1921 if (isBuiltin(types)) {
1909 setUseGvn(); 1922 setUseGvn();
1910 } else { 1923 } else {
1911 setAllSideEffects(); 1924 setAllSideEffects();
1912 } 1925 }
1913 } 1926 }
1914 1927
1915 bool isBuiltin(HTypeMap types) => operand.isNumber(types); 1928 bool isBuiltin(HTypeMap types) => operand.isNumber(types);
1916 1929
1917 HType computeTypeFromInputTypes(HTypeMap types) { 1930 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1918 HType operandType = types[operand]; 1931 HType operandType = types[operand];
1919 if (operandType.isNumber()) return operandType; 1932 if (operandType.isNumber()) return operandType;
1920 return HType.UNKNOWN; 1933 return HType.UNKNOWN;
1921 } 1934 }
1922 1935
1923 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1936 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1924 HTypeMap types) { 1937 HTypeMap types,
1938 Compiler compiler) {
1925 HType propagatedType = types[this]; 1939 HType propagatedType = types[this];
1926 // If the outgoing type should be a number (integer, double or both) we 1940 // If the outgoing type should be a number (integer, double or both) we
1927 // want the outgoing type to be the input too. 1941 // want the outgoing type to be the input too.
1928 // If we don't know the outgoing type we try to make it a number. 1942 // If we don't know the outgoing type we try to make it a number.
1929 if (propagatedType.isNumber()) return propagatedType; 1943 if (propagatedType.isNumber()) return propagatedType;
1930 if (propagatedType.isUnknown()) return HType.NUMBER; 1944 if (propagatedType.isUnknown()) return HType.NUMBER;
1931 return HType.UNKNOWN; 1945 return HType.UNKNOWN;
1932 } 1946 }
1933 1947
1934 HType computeLikelyType(HTypeMap types) => HType.NUMBER; 1948 HType computeLikelyType(HTypeMap types, Compiler compiler) => HType.NUMBER;
1935 1949
1936 abstract UnaryOperation operation(ConstantSystem constantSystem); 1950 abstract UnaryOperation operation(ConstantSystem constantSystem);
1937 } 1951 }
1938 1952
1939 class HNegate extends HInvokeUnary { 1953 class HNegate extends HInvokeUnary {
1940 HNegate(HStatic target, HInstruction input) : super(target, input); 1954 HNegate(HStatic target, HInstruction input) : super(target, input);
1941 accept(HVisitor visitor) => visitor.visitNegate(this); 1955 accept(HVisitor visitor) => visitor.visitNegate(this);
1942 1956
1943 UnaryOperation operation(ConstantSystem constantSystem) 1957 UnaryOperation operation(ConstantSystem constantSystem)
1944 => constantSystem.negate; 1958 => constantSystem.negate;
1945 int typeCode() => HInstruction.NEGATE_TYPECODE; 1959 int typeCode() => HInstruction.NEGATE_TYPECODE;
1946 bool typeEquals(other) => other is HNegate; 1960 bool typeEquals(other) => other is HNegate;
1947 bool dataEquals(HInstruction other) => true; 1961 bool dataEquals(HInstruction other) => true;
1948 } 1962 }
1949 1963
1950 class HBitNot extends HInvokeUnary { 1964 class HBitNot extends HInvokeUnary {
1951 HBitNot(HStatic target, HInstruction input) : super(target, input); 1965 HBitNot(HStatic target, HInstruction input) : super(target, input);
1952 accept(HVisitor visitor) => visitor.visitBitNot(this); 1966 accept(HVisitor visitor) => visitor.visitBitNot(this);
1953 1967
1954 HType computeTypeFromInputTypes(HTypeMap types) { 1968 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
1955 // All bitwise operations on primitive types either produce an 1969 // All bitwise operations on primitive types either produce an
1956 // integer or throw an error. 1970 // integer or throw an error.
1957 if (operand.isPrimitive(types)) return HType.INTEGER; 1971 if (operand.isPrimitive(types)) return HType.INTEGER;
1958 return HType.UNKNOWN; 1972 return HType.UNKNOWN;
1959 } 1973 }
1960 1974
1961 HType computeDesiredTypeForNonTargetInput(HInstruction input, 1975 HType computeDesiredTypeForNonTargetInput(HInstruction input,
1962 HTypeMap types) { 1976 HTypeMap types,
1977 Compiler compiler) {
1963 HType propagatedType = types[this]; 1978 HType propagatedType = types[this];
1964 // Bit operations only work on integers. If there is no desired output 1979 // Bit operations only work on integers. If there is no desired output
1965 // type or if it as a number we want to get an integer as input. 1980 // type or if it as a number we want to get an integer as input.
1966 if (propagatedType.isUnknown() || propagatedType.isNumber()) { 1981 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1967 return HType.INTEGER; 1982 return HType.INTEGER;
1968 } 1983 }
1969 return HType.UNKNOWN; 1984 return HType.UNKNOWN;
1970 } 1985 }
1971 1986
1972 UnaryOperation operation(ConstantSystem constantSystem) 1987 UnaryOperation operation(ConstantSystem constantSystem)
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
2088 class HNot extends HInstruction { 2103 class HNot extends HInstruction {
2089 HNot(HInstruction value) : super(<HInstruction>[value]); 2104 HNot(HInstruction value) : super(<HInstruction>[value]);
2090 void prepareGvn(HTypeMap types) { 2105 void prepareGvn(HTypeMap types) {
2091 assert(!hasSideEffects(types)); 2106 assert(!hasSideEffects(types));
2092 setUseGvn(); 2107 setUseGvn();
2093 } 2108 }
2094 2109
2095 HType get guaranteedType => HType.BOOLEAN; 2110 HType get guaranteedType => HType.BOOLEAN;
2096 2111
2097 // 'Not' only works on booleans. That's what we want as input. 2112 // 'Not' only works on booleans. That's what we want as input.
2098 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) { 2113 HType computeDesiredTypeForInput(HInstruction input,
2114 HTypeMap types,
2115 Compiler compiler) {
2099 return HType.BOOLEAN; 2116 return HType.BOOLEAN;
2100 } 2117 }
2101 2118
2102 accept(HVisitor visitor) => visitor.visitNot(this); 2119 accept(HVisitor visitor) => visitor.visitNot(this);
2103 int typeCode() => HInstruction.NOT_TYPECODE; 2120 int typeCode() => HInstruction.NOT_TYPECODE;
2104 bool typeEquals(other) => other is HNot; 2121 bool typeEquals(other) => other is HNot;
2105 bool dataEquals(HInstruction other) => true; 2122 bool dataEquals(HInstruction other) => true;
2106 } 2123 }
2107 2124
2108 /** 2125 /**
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
2160 void addInput(HInstruction input) { 2177 void addInput(HInstruction input) {
2161 assert(isInBasicBlock()); 2178 assert(isInBasicBlock());
2162 inputs.add(input); 2179 inputs.add(input);
2163 input.usedBy.add(this); 2180 input.usedBy.add(this);
2164 } 2181 }
2165 2182
2166 // Compute the (shared) type of the inputs if any. If all inputs 2183 // Compute the (shared) type of the inputs if any. If all inputs
2167 // have the same known type return it. If any two inputs have 2184 // have the same known type return it. If any two inputs have
2168 // different known types, we'll return a conflict -- otherwise we'll 2185 // different known types, we'll return a conflict -- otherwise we'll
2169 // simply return an unknown type. 2186 // simply return an unknown type.
2170 HType computeInputsType(bool ignoreUnknowns, HTypeMap types) { 2187 HType computeInputsType(bool ignoreUnknowns,
2188 HTypeMap types,
2189 Compiler compiler) {
2171 HType candidateType = HType.CONFLICTING; 2190 HType candidateType = HType.CONFLICTING;
2172 for (int i = 0, length = inputs.length; i < length; i++) { 2191 for (int i = 0, length = inputs.length; i < length; i++) {
2173 HType inputType = types[inputs[i]]; 2192 HType inputType = types[inputs[i]];
2174 if (ignoreUnknowns && inputType.isUnknown()) continue; 2193 if (ignoreUnknowns && inputType.isUnknown()) continue;
2175 // Phis need to combine the incoming types using the union operation. 2194 // Phis need to combine the incoming types using the union operation.
2176 // For example, if one incoming edge has type integer and the other has 2195 // For example, if one incoming edge has type integer and the other has
2177 // type double, then the phi is either an integer or double and thus has 2196 // type double, then the phi is either an integer or double and thus has
2178 // type number. 2197 // type number.
2179 candidateType = candidateType.union(inputType); 2198 candidateType = candidateType.union(inputType, compiler);
2180 if (candidateType.isUnknown()) return HType.UNKNOWN; 2199 if (candidateType.isUnknown()) return HType.UNKNOWN;
2181 } 2200 }
2182 return candidateType; 2201 return candidateType;
2183 } 2202 }
2184 2203
2185 HType computeTypeFromInputTypes(HTypeMap types) { 2204 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
2186 HType inputsType = computeInputsType(false, types); 2205 HType inputsType = computeInputsType(false, types, compiler);
2187 if (inputsType.isConflicting()) return HType.UNKNOWN; 2206 if (inputsType.isConflicting()) return HType.UNKNOWN;
2188 return inputsType; 2207 return inputsType;
2189 } 2208 }
2190 2209
2191 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) { 2210 HType computeDesiredTypeForInput(HInstruction input,
2211 HTypeMap types,
2212 Compiler compiler) {
2192 HType propagatedType = types[this]; 2213 HType propagatedType = types[this];
2193 // Best case scenario for a phi is, when all inputs have the same type. If 2214 // Best case scenario for a phi is, when all inputs have the same type. If
2194 // there is no desired outgoing type we therefore try to unify the input 2215 // there is no desired outgoing type we therefore try to unify the input
2195 // types (which is basically the [likelyType]). 2216 // types (which is basically the [likelyType]).
2196 if (propagatedType.isUnknown()) return computeLikelyType(types); 2217 if (propagatedType.isUnknown()) return computeLikelyType(types, compiler);
2197 // When the desired outgoing type is conflicting we don't need to give any 2218 // When the desired outgoing type is conflicting we don't need to give any
2198 // requirements on the inputs. 2219 // requirements on the inputs.
2199 if (propagatedType.isConflicting()) return HType.UNKNOWN; 2220 if (propagatedType.isConflicting()) return HType.UNKNOWN;
2200 // Otherwise the input type must match the desired outgoing type. 2221 // Otherwise the input type must match the desired outgoing type.
2201 return propagatedType; 2222 return propagatedType;
2202 } 2223 }
2203 2224
2204 HType computeLikelyType(HTypeMap types) { 2225 HType computeLikelyType(HTypeMap types, Compiler compiler) {
2205 HType agreedType = computeInputsType(true, types); 2226 HType agreedType = computeInputsType(true, types, compiler);
2206 if (agreedType.isConflicting()) return HType.UNKNOWN; 2227 if (agreedType.isConflicting()) return HType.UNKNOWN;
2207 // Don't be too restrictive. If the agreed type is integer or double just 2228 // Don't be too restrictive. If the agreed type is integer or double just
2208 // say that the likely type is number. If more is expected the type will be 2229 // say that the likely type is number. If more is expected the type will be
2209 // propagated back. 2230 // propagated back.
2210 if (agreedType.isNumber()) return HType.NUMBER; 2231 if (agreedType.isNumber()) return HType.NUMBER;
2211 return agreedType; 2232 return agreedType;
2212 } 2233 }
2213 2234
2214 bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR; 2235 bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
2215 2236
(...skipping 18 matching lines...) Expand all
2234 // Relational expressions can take part in global value numbering 2255 // Relational expressions can take part in global value numbering
2235 // and do not have any side-effects if we know all the inputs are 2256 // and do not have any side-effects if we know all the inputs are
2236 // numbers. This can be improved for at least equality. 2257 // numbers. This can be improved for at least equality.
2237 if (isBuiltin(types)) { 2258 if (isBuiltin(types)) {
2238 setUseGvn(); 2259 setUseGvn();
2239 } else { 2260 } else {
2240 setAllSideEffects(); 2261 setAllSideEffects();
2241 } 2262 }
2242 } 2263 }
2243 2264
2244 HType computeTypeFromInputTypes(HTypeMap types) { 2265 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
2245 if (left.isNumber(types) || usesBoolifiedInterceptor) return HType.BOOLEAN; 2266 if (left.isNumber(types) || usesBoolifiedInterceptor) return HType.BOOLEAN;
2246 return HType.UNKNOWN; 2267 return HType.UNKNOWN;
2247 } 2268 }
2248 2269
2249 HType get guaranteedType { 2270 HType get guaranteedType {
2250 if (usesBoolifiedInterceptor) return HType.BOOLEAN; 2271 if (usesBoolifiedInterceptor) return HType.BOOLEAN;
2251 return HType.UNKNOWN; 2272 return HType.UNKNOWN;
2252 } 2273 }
2253 2274
2254 HType computeDesiredTypeForNonTargetInput(HInstruction input, 2275 HType computeDesiredTypeForNonTargetInput(HInstruction input,
2255 HTypeMap types) { 2276 HTypeMap types,
2277 Compiler compiler) {
2256 HType propagatedType = types[this]; 2278 HType propagatedType = types[this];
2257 // For all relational operations exept HEquals, we expect to get numbers 2279 // For all relational operations exept HEquals, we expect to get numbers
2258 // only. With numbers the outgoing type is a boolean. If something else 2280 // only. With numbers the outgoing type is a boolean. If something else
2259 // is desired, then numbers are incorrect, though. 2281 // is desired, then numbers are incorrect, though.
2260 if (propagatedType.isUnknown() || propagatedType.isBoolean()) { 2282 if (propagatedType.isUnknown() || propagatedType.isBoolean()) {
2261 if (left.isTypeUnknown(types) || left.isNumber(types)) { 2283 if (left.isTypeUnknown(types) || left.isNumber(types)) {
2262 return HType.NUMBER; 2284 return HType.NUMBER;
2263 } 2285 }
2264 } 2286 }
2265 return HType.UNKNOWN; 2287 return HType.UNKNOWN;
2266 } 2288 }
2267 2289
2268 HType computeLikelyType(HTypeMap types) => HType.BOOLEAN; 2290 HType computeLikelyType(HTypeMap types, Compiler compiler) => HType.BOOLEAN;
2269 2291
2270 bool isBuiltin(HTypeMap types) 2292 bool isBuiltin(HTypeMap types)
2271 => left.isNumber(types) && right.isNumber(types); 2293 => left.isNumber(types) && right.isNumber(types);
2272 // TODO(1603): the class should be marked as abstract. 2294 // TODO(1603): the class should be marked as abstract.
2273 abstract BinaryOperation operation(ConstantSystem constantSystem); 2295 abstract BinaryOperation operation(ConstantSystem constantSystem);
2274 } 2296 }
2275 2297
2276 class HEquals extends HRelational { 2298 class HEquals extends HRelational {
2277 HEquals(HStatic target, HInstruction left, HInstruction right) 2299 HEquals(HStatic target, HInstruction left, HInstruction right)
2278 : super(target, left, right); 2300 : super(target, left, right);
2279 accept(HVisitor visitor) => visitor.visitEquals(this); 2301 accept(HVisitor visitor) => visitor.visitEquals(this);
2280 2302
2281 bool isBuiltin(HTypeMap types) { 2303 bool isBuiltin(HTypeMap types) {
2282 // All primitive types have === semantics. 2304 // All primitive types have === semantics.
2283 // Note that this includes all constants except the user-constructed 2305 // Note that this includes all constants except the user-constructed
2284 // objects. 2306 // objects.
2285 return types[left].isPrimitiveOrNull() || right.isConstantNull(); 2307 return types[left].isPrimitiveOrNull() || right.isConstantNull();
2286 } 2308 }
2287 2309
2288 HType computeTypeFromInputTypes(HTypeMap types) { 2310 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
2289 if (isBuiltin(types) || usesBoolifiedInterceptor) return HType.BOOLEAN; 2311 if (isBuiltin(types) || usesBoolifiedInterceptor) return HType.BOOLEAN;
2290 return HType.UNKNOWN; 2312 return HType.UNKNOWN;
2291 } 2313 }
2292 2314
2293 HType computeDesiredTypeForNonTargetInput(HInstruction input, 2315 HType computeDesiredTypeForNonTargetInput(HInstruction input,
2294 HTypeMap types) { 2316 HTypeMap types,
2317 Compiler compiler) {
2295 HType propagatedType = types[this]; 2318 HType propagatedType = types[this];
2296 if (input == left && types[right].isUseful()) { 2319 if (input == left && types[right].isUseful()) {
2297 // All our useful types have === semantics. But we don't want to 2320 // All our useful types have === semantics. But we don't want to
2298 // speculatively test for all possible types. Therefore we try to match 2321 // speculatively test for all possible types. Therefore we try to match
2299 // the two types. That is, if we see x == 3, then we speculatively test 2322 // the two types. That is, if we see x == 3, then we speculatively test
2300 // if x is a number and bailout if it isn't. 2323 // if x is a number and bailout if it isn't.
2301 // If right is a number we don't need more than a number (no need to match 2324 // If right is a number we don't need more than a number (no need to match
2302 // the exact type of right). 2325 // the exact type of right).
2303 if (right.isNumber(types)) return HType.NUMBER; 2326 if (right.isNumber(types)) return HType.NUMBER;
2304 // String equality testing is much more common than array equality 2327 // String equality testing is much more common than array equality
(...skipping 20 matching lines...) Expand all
2325 } 2348 }
2326 2349
2327 class HIdentity extends HRelational { 2350 class HIdentity extends HRelational {
2328 HIdentity(HStatic target, HInstruction left, HInstruction right) 2351 HIdentity(HStatic target, HInstruction left, HInstruction right)
2329 : super(target, left, right); 2352 : super(target, left, right);
2330 accept(HVisitor visitor) => visitor.visitIdentity(this); 2353 accept(HVisitor visitor) => visitor.visitIdentity(this);
2331 2354
2332 bool isBuiltin(HTypeMap types) => true; 2355 bool isBuiltin(HTypeMap types) => true;
2333 2356
2334 HType get guaranteedType => HType.BOOLEAN; 2357 HType get guaranteedType => HType.BOOLEAN;
2335 HType computeTypeFromInputTypes(HTypeMap types) 2358 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler)
2336 => HType.BOOLEAN; 2359 => HType.BOOLEAN;
2337 // Note that the identity operator really does not care for its input types. 2360 // Note that the identity operator really does not care for its input types.
2338 HType computeDesiredTypeForInput(HInstruction input, HTypeMap types) 2361 HType computeDesiredTypeForInput(HInstruction input,
2339 => HType.UNKNOWN; 2362 HTypeMap types,
2363 Compiler compiler) {
2364 return HType.UNKNOWN;
2365 }
2340 2366
2341 BinaryOperation operation(ConstantSystem constantSystem) 2367 BinaryOperation operation(ConstantSystem constantSystem)
2342 => constantSystem.identity; 2368 => constantSystem.identity;
2343 int typeCode() => HInstruction.IDENTITY_TYPECODE; 2369 int typeCode() => HInstruction.IDENTITY_TYPECODE;
2344 bool typeEquals(other) => other is HIdentity; 2370 bool typeEquals(other) => other is HIdentity;
2345 bool dataEquals(HInstruction other) => true; 2371 bool dataEquals(HInstruction other) => true;
2346 } 2372 }
2347 2373
2348 class HGreater extends HRelational { 2374 class HGreater extends HRelational {
2349 HGreater(HStatic target, HInstruction left, HInstruction right) 2375 HGreater(HStatic target, HInstruction left, HInstruction right)
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
2490 setUseGvn(); 2516 setUseGvn();
2491 } else { 2517 } else {
2492 setAllSideEffects(); 2518 setAllSideEffects();
2493 } 2519 }
2494 } 2520 }
2495 2521
2496 HInstruction get receiver => inputs[1]; 2522 HInstruction get receiver => inputs[1];
2497 HInstruction get index => inputs[2]; 2523 HInstruction get index => inputs[2];
2498 2524
2499 HType computeDesiredTypeForNonTargetInput(HInstruction input, 2525 HType computeDesiredTypeForNonTargetInput(HInstruction input,
2500 HTypeMap types) { 2526 HTypeMap types,
2527 Compiler compiler) {
2501 if (input == receiver && 2528 if (input == receiver &&
2502 (index.isTypeUnknown(types) || index.isNumber(types))) { 2529 (index.isTypeUnknown(types) || index.isNumber(types))) {
2503 return HType.INDEXABLE_PRIMITIVE; 2530 return HType.INDEXABLE_PRIMITIVE;
2504 } 2531 }
2505 // The index should be an int when the receiver is a string or array. 2532 // The index should be an int when the receiver is a string or array.
2506 // However it turns out that inserting an integer check in the optimized 2533 // However it turns out that inserting an integer check in the optimized
2507 // version is cheaper than having another bailout case. This is true, 2534 // version is cheaper than having another bailout case. This is true,
2508 // because the integer check will simply throw if it fails. 2535 // because the integer check will simply throw if it fails.
2509 return HType.UNKNOWN; 2536 return HType.UNKNOWN;
2510 } 2537 }
(...skipping 25 matching lines...) Expand all
2536 setChangesIndex(); 2563 setChangesIndex();
2537 } else { 2564 } else {
2538 setAllSideEffects(); 2565 setAllSideEffects();
2539 } 2566 }
2540 } 2567 }
2541 2568
2542 // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign] 2569 // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign]
2543 // is never used as input. 2570 // is never used as input.
2544 2571
2545 HType computeDesiredTypeForNonTargetInput(HInstruction input, 2572 HType computeDesiredTypeForNonTargetInput(HInstruction input,
2546 HTypeMap types) { 2573 HTypeMap types,
2574 Compiler compiler) {
2547 if (input == receiver && 2575 if (input == receiver &&
2548 (index.isTypeUnknown(types) || index.isNumber(types))) { 2576 (index.isTypeUnknown(types) || index.isNumber(types))) {
2549 return HType.MUTABLE_ARRAY; 2577 return HType.MUTABLE_ARRAY;
2550 } 2578 }
2551 // The index should be an int when the receiver is a string or array. 2579 // The index should be an int when the receiver is a string or array.
2552 // However it turns out that inserting an integer check in the optimized 2580 // However it turns out that inserting an integer check in the optimized
2553 // version is cheaper than having another bailout case. This is true, 2581 // version is cheaper than having another bailout case. This is true,
2554 // because the integer check will simply throw if it fails. 2582 // because the integer check will simply throw if it fails.
2555 return HType.UNKNOWN; 2583 return HType.UNKNOWN;
2556 } 2584 }
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after
2976 HBasicBlock get start => expression.start; 3004 HBasicBlock get start => expression.start;
2977 HBasicBlock get end { 3005 HBasicBlock get end {
2978 // We don't create a switch block if there are no cases. 3006 // We don't create a switch block if there are no cases.
2979 assert(!statements.isEmpty); 3007 assert(!statements.isEmpty);
2980 return statements.last.end; 3008 return statements.last.end;
2981 } 3009 }
2982 3010
2983 bool accept(HStatementInformationVisitor visitor) => 3011 bool accept(HStatementInformationVisitor visitor) =>
2984 visitor.visitSwitchInfo(this); 3012 visitor.visitSwitchInfo(this);
2985 } 3013 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/ssa/codegen.dart ('k') | lib/compiler/implementation/ssa/optimize.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698