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

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

Issue 9956152: Reapply "Refactor type propagation." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Apply fix. Created 8 years, 8 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
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, 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 interface HVisitor<R> { 5 interface HVisitor<R> {
6 R visitAdd(HAdd node); 6 R visitAdd(HAdd node);
7 R visitBitAnd(HBitAnd node); 7 R visitBitAnd(HBitAnd node);
8 R visitBitNot(HBitNot node); 8 R visitBitNot(HBitNot node);
9 R visitBitOr(HBitOr node); 9 R visitBitOr(HBitOr node);
10 R visitBitXor(HBitXor node); 10 R visitBitXor(HBitXor node);
(...skipping 770 matching lines...) Expand 10 before | Expand all | Expand 10 after
781 bool isUnknown() => this === UNKNOWN; 781 bool isUnknown() => this === UNKNOWN;
782 bool isBoolean() => this === BOOLEAN; 782 bool isBoolean() => this === BOOLEAN;
783 bool isInteger() => this === INTEGER; 783 bool isInteger() => this === INTEGER;
784 bool isDouble() => this === DOUBLE; 784 bool isDouble() => this === DOUBLE;
785 bool isString() => this === STRING; 785 bool isString() => this === STRING;
786 bool isArray() => (this.flag & FLAG_READABLE_ARRAY) != 0; 786 bool isArray() => (this.flag & FLAG_READABLE_ARRAY) != 0;
787 bool isMutableArray() => this === MUTABLE_ARRAY; 787 bool isMutableArray() => this === MUTABLE_ARRAY;
788 bool isNumber() => (this.flag & (FLAG_INTEGER | FLAG_DOUBLE)) != 0; 788 bool isNumber() => (this.flag & (FLAG_INTEGER | FLAG_DOUBLE)) != 0;
789 bool isStringOrArray() => 789 bool isStringOrArray() =>
790 (this.flag & (FLAG_STRING | FLAG_READABLE_ARRAY)) != 0; 790 (this.flag & (FLAG_STRING | FLAG_READABLE_ARRAY)) != 0;
791 bool isKnown() => this !== UNKNOWN && this !== CONFLICTING; 791 /** A type is useful it is not unknown and not conflicting. */
792 bool isUseful() => this !== UNKNOWN && this !== CONFLICTING;
792 793
793 static HType getTypeFromFlag(int flag) { 794 static HType getTypeFromFlag(int flag) {
794 if (flag === CONFLICTING.flag) return CONFLICTING; 795 if (flag === CONFLICTING.flag) return CONFLICTING;
795 if (flag === UNKNOWN.flag) return UNKNOWN; 796 if (flag === UNKNOWN.flag) return UNKNOWN;
796 if (flag === BOOLEAN.flag) return BOOLEAN; 797 if (flag === BOOLEAN.flag) return BOOLEAN;
797 if (flag === INTEGER.flag) return INTEGER; 798 if (flag === INTEGER.flag) return INTEGER;
798 if (flag === DOUBLE.flag) return DOUBLE; 799 if (flag === DOUBLE.flag) return DOUBLE;
799 if (flag === STRING.flag) return STRING; 800 if (flag === STRING.flag) return STRING;
800 if (flag === READABLE_ARRAY.flag) return READABLE_ARRAY; 801 if (flag === READABLE_ARRAY.flag) return READABLE_ARRAY;
801 if (flag === MUTABLE_ARRAY.flag) return MUTABLE_ARRAY; 802 if (flag === MUTABLE_ARRAY.flag) return MUTABLE_ARRAY;
(...skipping 27 matching lines...) Expand all
829 final int id; 830 final int id;
830 static int idCounter; 831 static int idCounter;
831 832
832 final List<HInstruction> inputs; 833 final List<HInstruction> inputs;
833 final List<HInstruction> usedBy; 834 final List<HInstruction> usedBy;
834 835
835 HBasicBlock block; 836 HBasicBlock block;
836 HInstruction previous = null; 837 HInstruction previous = null;
837 HInstruction next = null; 838 HInstruction next = null;
838 int flags = 0; 839 int flags = 0;
839 HType type = HType.UNKNOWN;
840 840
841 // Changes flags. 841 // Changes flags.
842 static final int FLAG_CHANGES_SOMETHING = 0; 842 static final int FLAG_CHANGES_SOMETHING = 0;
843 static final int FLAG_CHANGES_COUNT = FLAG_CHANGES_SOMETHING + 1; 843 static final int FLAG_CHANGES_COUNT = FLAG_CHANGES_SOMETHING + 1;
844 844
845 // Depends flags (one for each changes flag). 845 // Depends flags (one for each changes flag).
846 static final int FLAG_DEPENDS_ON_SOMETHING = FLAG_CHANGES_COUNT; 846 static final int FLAG_DEPENDS_ON_SOMETHING = FLAG_CHANGES_COUNT;
847 847
848 // Other flags. 848 // Other flags.
849 static final int FLAG_USE_GVN = FLAG_DEPENDS_ON_SOMETHING + 1; 849 static final int FLAG_USE_GVN = FLAG_DEPENDS_ON_SOMETHING + 1;
850 850
851 HInstruction(this.inputs) : id = idCounter++, usedBy = <HInstruction>[]; 851 HInstruction(this.inputs)
852 : id = idCounter++,
853 usedBy = <HInstruction>[] {
854 if (guaranteedType.isUseful()) propagatedType = guaranteedType;
855 }
852 856
853 int hashCode() => id; 857 int hashCode() => id;
854 858
855 bool getFlag(int position) => (flags & (1 << position)) != 0; 859 bool getFlag(int position) => (flags & (1 << position)) != 0;
856 void setFlag(int position) { flags |= (1 << position); } 860 void setFlag(int position) { flags |= (1 << position); }
857 void clearFlag(int position) { flags &= ~(1 << position); } 861 void clearFlag(int position) { flags &= ~(1 << position); }
858 862
859 static int computeDependsOnFlags(int flags) => flags << FLAG_CHANGES_COUNT; 863 static int computeDependsOnFlags(int flags) => flags << FLAG_CHANGES_COUNT;
860 864
861 int getChangesFlags() => flags & ((1 << FLAG_CHANGES_COUNT) - 1); 865 int getChangesFlags() => flags & ((1 << FLAG_CHANGES_COUNT) - 1);
862 bool hasSideEffects() => getChangesFlags() != 0; 866 bool hasSideEffects() => getChangesFlags() != 0;
863 void prepareGvn() { setAllSideEffects(); } 867 void prepareGvn() { setAllSideEffects(); }
864 868
865 void setAllSideEffects() { flags |= ((1 << FLAG_CHANGES_COUNT) - 1); } 869 void setAllSideEffects() { flags |= ((1 << FLAG_CHANGES_COUNT) - 1); }
866 void clearAllSideEffects() { flags &= ~((1 << FLAG_CHANGES_COUNT) - 1); } 870 void clearAllSideEffects() { flags &= ~((1 << FLAG_CHANGES_COUNT) - 1); }
867 871
868 bool useGvn() => getFlag(FLAG_USE_GVN); 872 bool useGvn() => getFlag(FLAG_USE_GVN);
869 void setUseGvn() { setFlag(FLAG_USE_GVN); } 873 void setUseGvn() { setFlag(FLAG_USE_GVN); }
870 // Does this node potentially affect control flow. 874 // Does this node potentially affect control flow.
871 bool isControlFlow() => false; 875 bool isControlFlow() => false;
872 876
873 bool isArray() => type.isArray(); 877 // All isFunctions work on the propagated types.
874 bool isMutableArray() => type.isMutableArray(); 878 bool isArray() => propagatedType.isArray();
875 bool isBoolean() => type.isBoolean(); 879 bool isMutableArray() => propagatedType.isMutableArray();
876 bool isInteger() => type.isInteger(); 880 bool isBoolean() => propagatedType.isBoolean();
877 bool isNumber() => type.isNumber(); 881 bool isInteger() => propagatedType.isInteger();
878 bool isString() => type.isString(); 882 bool isDouble() => propagatedType.isDouble();
879 bool isTypeUnknown() => type.isUnknown(); 883 bool isNumber() => propagatedType.isNumber();
880 bool isStringOrArray() => type.isStringOrArray(); 884 bool isString() => propagatedType.isString();
885 bool isTypeUnknown() => propagatedType.isUnknown();
886 bool isStringOrArray() => propagatedType.isStringOrArray();
881 887
882 // Compute the type of the instruction. 888 /**
883 HType computeType() => HType.UNKNOWN; 889 * This is the type the instruction is guaranteed to have. It does not
890 * take any propagation into account.
891 */
892 HType get guaranteedType() => HType.UNKNOWN;
893 bool hasGuaranteedType() => !guaranteedType.isUnknown();
884 894
885 HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN; 895 /**
896 * The [propagatedType] is the type the instruction is assumed to have.
897 * Without speculative type assumptions it is computed frome the propagated
898 * type of the instruction's inputs and does not any guess work.
899 *
900 * With speculative types [computeTypeFromInputTypes()] and [propagatedType]
901 * may differ. In this case the instruction's type must be guarded.
902 *
903 * Note that the [propagatedType] may only be set to [HType.CONFLICTING] with
904 * speculative types (as otherwise the instruction either sets the output
905 * type to [HType.UNKNOWN] or a specific type.
906 */
907 HType propagatedType = HType.UNKNOWN;
886 908
887 // Returns whether the instruction does produce the type it claims. 909 /**
888 // For most instructions, this returns false. A type guard will be 910 * Some instructions have a good idea of their return type, but cannot
889 // inserted to make sure the users get the right type in. 911 * guarantee the type. The [likelyType] does not need to be more specialized
890 bool hasExpectedType() => false; 912 * than the [propagatedType].
913 *
914 * Examples: the [likelyType] of [:x == y:] is a boolean. In most cases this
915 * cannot be guaranteed, but when merging types we still want to use this
916 * information.
917 *
918 * Similarily the [HAdd] instruction is likely a number. Note that, even if
919 * the [propagatedType] is already set to integer, the [likelyType] still
920 * might just return the number type.
921 */
922 HType get likelyType() => propagatedType;
923
924 /**
925 * Compute the type of the instruction by propagating the input types through
926 * the instruction.
927 *
928 * By default just copy the guaranteed type.
929 */
930 HType computeTypeFromInputTypes() => guaranteedType;
931
932 /**
933 * Compute the desired type for the the given [input]. Aside from using
934 * other inputs to compute the desired type one should also use
935 * the [propagatedType] which, during the invocation of this method,
936 * represents the desired type of [this].
937 */
938 HType computeDesiredTypeForInput(HInstruction input) => HType.UNKNOWN;
891 939
892 bool isInBasicBlock() => block !== null; 940 bool isInBasicBlock() => block !== null;
893 941
894 String inputsToString() { 942 String inputsToString() {
895 void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) { 943 void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) {
896 for (int i = 0; i < list.length; i++) { 944 for (int i = 0; i < list.length; i++) {
897 if (i != 0) buffer.add(', '); 945 if (i != 0) buffer.add(', ');
898 buffer.add("@${list[i].id}"); 946 buffer.add("@${list[i].id}");
899 } 947 }
900 } 948 }
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 bool isCodeMotionInvariant() => false; 1042 bool isCodeMotionInvariant() => false;
995 } 1043 }
996 1044
997 class HBoolify extends HInstruction { 1045 class HBoolify extends HInstruction {
998 HBoolify(HInstruction value) : super(<HInstruction>[value]); 1046 HBoolify(HInstruction value) : super(<HInstruction>[value]);
999 void prepareGvn() { 1047 void prepareGvn() {
1000 assert(!hasSideEffects()); 1048 assert(!hasSideEffects());
1001 setUseGvn(); 1049 setUseGvn();
1002 } 1050 }
1003 1051
1004 HType computeType() => HType.BOOLEAN; 1052 HType get guaranteedType() => HType.BOOLEAN;
1005 bool hasExpectedType() => true;
1006 1053
1007 accept(HVisitor visitor) => visitor.visitBoolify(this); 1054 accept(HVisitor visitor) => visitor.visitBoolify(this);
1008 int typeCode() => 0; 1055 int typeCode() => 0;
1009 bool typeEquals(other) => other is HBoolify; 1056 bool typeEquals(other) => other is HBoolify;
1010 bool dataEquals(HInstruction other) => true; 1057 bool dataEquals(HInstruction other) => true;
1011 } 1058 }
1012 1059
1013 class HCheck extends HInstruction { 1060 class HCheck extends HInstruction {
1014 HCheck(inputs) : super(inputs); 1061 HCheck(inputs) : super(inputs);
1015 1062
1016 // TODO(floitsch): make class abstract instead of adding an abstract method. 1063 // TODO(floitsch): make class abstract instead of adding an abstract method.
1017 abstract accept(HVisitor visitor); 1064 abstract accept(HVisitor visitor);
1018 1065
1019 bool isControlFlow() => true; 1066 bool isControlFlow() => true;
1020 } 1067 }
1021 1068
1022 class HTypeGuard extends HInstruction { 1069 class HTypeGuard extends HInstruction {
1023 int state; 1070 int state;
1024 HTypeGuard(int this.state, List<HInstruction> env) : super(env); 1071 HTypeGuard(int this.state, List<HInstruction> env) : super(env);
1025 1072
1026 void prepareGvn() { 1073 void prepareGvn() {
1027 assert(!hasSideEffects()); 1074 assert(!hasSideEffects());
1028 setUseGvn(); 1075 setUseGvn();
1029 } 1076 }
1030 1077
1031 HInstruction get guarded() => inputs.last(); 1078 HInstruction get guarded() => inputs.last();
1032 1079
1033 HType computeType() => type;
1034 bool hasExpectedType() => true;
1035
1036 bool isControlFlow() => true; 1080 bool isControlFlow() => true;
1037 1081
1038 accept(HVisitor visitor) => visitor.visitTypeGuard(this); 1082 accept(HVisitor visitor) => visitor.visitTypeGuard(this);
1039 int typeCode() => 1; 1083 int typeCode() => 1;
1040 bool typeEquals(other) => other is HTypeGuard; 1084 bool typeEquals(other) => other is HTypeGuard;
1041 bool dataEquals(HTypeGuard other) => type == other.type; 1085 bool dataEquals(HTypeGuard other) => propagatedType == other.propagatedType;
1042 } 1086 }
1043 1087
1044 class HBoundsCheck extends HCheck { 1088 class HBoundsCheck extends HCheck {
1045 HBoundsCheck(length, index) : super(<HInstruction>[length, index]) { 1089 HBoundsCheck(length, index) : super(<HInstruction>[length, index]);
1046 type = HType.INTEGER;
1047 }
1048 1090
1049 HInstruction get length() => inputs[0]; 1091 HInstruction get length() => inputs[0];
1050 HInstruction get index() => inputs[1]; 1092 HInstruction get index() => inputs[1];
1051 1093
1052 void prepareGvn() { 1094 void prepareGvn() {
1053 assert(!hasSideEffects()); 1095 assert(!hasSideEffects());
1054 setUseGvn(); 1096 setUseGvn();
1055 } 1097 }
1056 1098
1057 HType computeType() => HType.INTEGER; 1099 HType get guaranteedType() => HType.INTEGER;
1058 bool hasExpectedType() => true;
1059 1100
1060 accept(HVisitor visitor) => visitor.visitBoundsCheck(this); 1101 accept(HVisitor visitor) => visitor.visitBoundsCheck(this);
1061 int typeCode() => 2; 1102 int typeCode() => 2;
1062 bool typeEquals(other) => other is HBoundsCheck; 1103 bool typeEquals(other) => other is HBoundsCheck;
1063 bool dataEquals(HInstruction other) => true; 1104 bool dataEquals(HInstruction other) => true;
1064 } 1105 }
1065 1106
1066 class HIntegerCheck extends HCheck { 1107 class HIntegerCheck extends HCheck {
1067 HIntegerCheck(value) : super(<HInstruction>[value]); 1108 HIntegerCheck(value) : super(<HInstruction>[value]);
1068 1109
1069 HInstruction get value() => inputs[0]; 1110 HInstruction get value() => inputs[0];
1070 1111
1071 void prepareGvn() { 1112 void prepareGvn() {
1072 assert(!hasSideEffects()); 1113 assert(!hasSideEffects());
1073 setUseGvn(); 1114 setUseGvn();
1074 } 1115 }
1075 1116
1076 HType computeType() => HType.INTEGER; 1117 HType get guaranteedType() => HType.INTEGER;
1077 bool hasExpectedType() => true;
1078 1118
1079 accept(HVisitor visitor) => visitor.visitIntegerCheck(this); 1119 accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
1080 int typeCode() => 3; 1120 int typeCode() => 3;
1081 bool typeEquals(other) => other is HIntegerCheck; 1121 bool typeEquals(other) => other is HIntegerCheck;
1082 bool dataEquals(HInstruction other) => true; 1122 bool dataEquals(HInstruction other) => true;
1083 } 1123 }
1084 1124
1085 class HConditionalBranch extends HControlFlow { 1125 class HConditionalBranch extends HControlFlow {
1086 HConditionalBranch(inputs) : super(inputs); 1126 HConditionalBranch(inputs) : super(inputs);
1087 HInstruction get condition() => inputs[0]; 1127 HInstruction get condition() => inputs[0];
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1172 Element get element() => target.element; 1212 Element get element() => target.element;
1173 HStatic get target() => inputs[0]; 1213 HStatic get target() => inputs[0];
1174 1214
1175 bool isArrayConstructor() { 1215 bool isArrayConstructor() {
1176 // TODO(ngeoffray): This is not the right way to do the check, 1216 // TODO(ngeoffray): This is not the right way to do the check,
1177 // nor the right place. We need to move it to a phase. 1217 // nor the right place. We need to move it to a phase.
1178 return (element.isFactoryConstructor() 1218 return (element.isFactoryConstructor()
1179 && element.enclosingElement.name.slowToString() == 'List'); 1219 && element.enclosingElement.name.slowToString() == 'List');
1180 } 1220 }
1181 1221
1182 HType computeType() { 1222 HType get guaranteedType() {
1183 if (isArrayConstructor()) { 1223 if (isArrayConstructor()) {
1184 return HType.MUTABLE_ARRAY; 1224 return HType.MUTABLE_ARRAY;
1185 } 1225 }
1186 return HType.UNKNOWN; 1226 return HType.UNKNOWN;
1187 } 1227 }
1188 1228
1229 HType computeDesiredTypeForInput(HInstruction input) {
1230 // TODO(floitsch): we want the target to be a function.
1231 if (input == target) return HType.UNKNOWN;
1232 return computeDesiredTypeForNonTargetInput(input);
1233 }
1234
1235 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1236 return HType.UNKNOWN;
1237 }
1238
1189 bool get builtin() => isArrayConstructor(); 1239 bool get builtin() => isArrayConstructor();
1190 bool hasExpectedType() => isArrayConstructor();
1191 } 1240 }
1192 1241
1193 class HInvokeSuper extends HInvokeStatic { 1242 class HInvokeSuper extends HInvokeStatic {
1194 HInvokeSuper(selector, inputs) : super(selector, inputs); 1243 HInvokeSuper(selector, inputs) : super(selector, inputs);
1195 toString() => 'invoke super: ${element.name}'; 1244 toString() => 'invoke super: ${element.name}';
1196 accept(HVisitor visitor) => visitor.visitInvokeSuper(this); 1245 accept(HVisitor visitor) => visitor.visitInvokeSuper(this);
1197 } 1246 }
1198 1247
1199 class HInvokeInterceptor extends HInvokeStatic { 1248 class HInvokeInterceptor extends HInvokeStatic {
1200 final SourceString name; 1249 final SourceString name;
1201 final bool getter; 1250 final bool getter;
1202 1251
1203 HInvokeInterceptor(Selector selector, 1252 HInvokeInterceptor(Selector selector,
1204 SourceString this.name, 1253 SourceString this.name,
1205 bool this.getter, 1254 bool this.getter,
1206 List<HInstruction> inputs) 1255 List<HInstruction> inputs)
1207 : super(selector, inputs); 1256 : super(selector, inputs);
1208 toString() => 'invoke interceptor: ${element.name}'; 1257 toString() => 'invoke interceptor: ${element.name}';
1209 accept(HVisitor visitor) => visitor.visitInvokeInterceptor(this); 1258 accept(HVisitor visitor) => visitor.visitInvokeInterceptor(this);
1210 1259
1260 bool isLengthGetterOnStringOrArray() {
1261 return getter
1262 && name == const SourceString('length')
1263 && inputs[1].isStringOrArray();
1264 }
1265
1211 String get builtinJsName() { 1266 String get builtinJsName() {
1212 if (getter 1267 if (isLengthGetterOnStringOrArray()) {
1213 && name == const SourceString('length')
1214 && inputs[1].isStringOrArray()) {
1215 return 'length'; 1268 return 'length';
1216 } else if (name == const SourceString('add') 1269 } else if (name == const SourceString('add')
1217 && inputs[1].isMutableArray()) { 1270 && inputs[1].isMutableArray()) {
1218 return 'push'; 1271 return 'push';
1219 } else if (name == const SourceString('removeLast') 1272 } else if (name == const SourceString('removeLast')
1220 && inputs[1].isMutableArray()) { 1273 && inputs[1].isMutableArray()) {
1221 return 'pop'; 1274 return 'pop';
1222 } 1275 }
1223 return null; 1276 return null;
1224 } 1277 }
1225 1278
1226 HType computeType() { 1279 HType get guaranteedType() => HType.UNKNOWN;
1227 if (getter 1280
1228 && name == const SourceString('length') 1281 HType get likelyType() {
1229 && inputs[1].isStringOrArray()) { 1282 // In general a length getter or method returns an int.
1230 return HType.INTEGER; 1283 if (name == const SourceString('length')) return HType.INTEGER;
1231 }
1232 return HType.UNKNOWN; 1284 return HType.UNKNOWN;
1233 } 1285 }
1234 1286
1235 HType computeDesiredInputType(HInstruction input) { 1287 HType computeTypeFromInputTypes() {
1236 if (input == inputs[0]) return HType.UNKNOWN; 1288 if (isLengthGetterOnStringOrArray()) return HType.INTEGER;
1289 return HType.UNKNOWN;
1290 }
1291
1292 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1293 // If the first argument is a string or an array and we invoke methods
1294 // on it that mutate it, then we want to restrict the incoming type to be
1295 // a mutable array.
1237 if (input == inputs[1] && input.isStringOrArray()) { 1296 if (input == inputs[1] && input.isStringOrArray()) {
1238 if (name == const SourceString('add') 1297 if (name == const SourceString('add')
1239 || name == const SourceString('removeLast')) { 1298 || name == const SourceString('removeLast')) {
1240 return HType.MUTABLE_ARRAY; 1299 return HType.MUTABLE_ARRAY;
1241 } 1300 }
1242 } 1301 }
1243 return HType.UNKNOWN; 1302 return HType.UNKNOWN;
1244 } 1303 }
1245 1304
1246 bool hasExpectedType() => builtinJsName != null;
1247
1248 void prepareGvn() { 1305 void prepareGvn() {
1249 if (builtinJsName == 'length') { 1306 if (isLengthGetterOnStringOrArray()) {
1250 clearAllSideEffects(); 1307 clearAllSideEffects();
1251 } else { 1308 } else {
1252 setAllSideEffects(); 1309 setAllSideEffects();
1253 } 1310 }
1254 } 1311 }
1255 1312
1256 int typeCode() => 4; 1313 int typeCode() => 4;
1257 bool typeEquals(other) => other is HInvokeInterceptor; 1314 bool typeEquals(other) => other is HInvokeInterceptor;
1258 bool dataEquals(HInvokeInterceptor other) { 1315 bool dataEquals(HInvokeInterceptor other) {
1259 return builtinJsName == other.builtinJsName && name == other.name; 1316 return builtinJsName == other.builtinJsName && name == other.name;
(...skipping 22 matching lines...) Expand all
1282 accept(HVisitor visitor) => visitor.visitFieldSet(this); 1339 accept(HVisitor visitor) => visitor.visitFieldSet(this);
1283 1340
1284 void prepareGvn() { 1341 void prepareGvn() {
1285 // TODO(ngeoffray): implement more fine grain side effects. 1342 // TODO(ngeoffray): implement more fine grain side effects.
1286 setAllSideEffects(); 1343 setAllSideEffects();
1287 } 1344 }
1288 } 1345 }
1289 1346
1290 class HForeign extends HInstruction { 1347 class HForeign extends HInstruction {
1291 final DartString code; 1348 final DartString code;
1292 final DartString declaredType; 1349 final HType foreignType;
1293 HForeign(this.code, this.declaredType, List<HInstruction> inputs) 1350 HForeign(this.code, DartString declaredType, List<HInstruction> inputs)
1294 : super(inputs); 1351 : foreignType = computeTypeFromDeclaredType(declaredType),
1352 super(inputs);
1295 accept(HVisitor visitor) => visitor.visitForeign(this); 1353 accept(HVisitor visitor) => visitor.visitForeign(this);
1296 1354
1297 HType computeType() { 1355 static HType computeTypeFromDeclaredType(DartString declaredType) {
1298 if (declaredType.slowToString() == 'bool') return HType.BOOLEAN; 1356 if (declaredType.slowToString() == 'bool') return HType.BOOLEAN;
1299 if (declaredType.slowToString() == 'int') return HType.INTEGER; 1357 if (declaredType.slowToString() == 'int') return HType.INTEGER;
1300 if (declaredType.slowToString() == 'num') return HType.NUMBER; 1358 if (declaredType.slowToString() == 'num') return HType.NUMBER;
1301 if (declaredType.slowToString() == 'String') return HType.STRING; 1359 if (declaredType.slowToString() == 'String') return HType.STRING;
1302 return HType.UNKNOWN; 1360 return HType.UNKNOWN;
1303 } 1361 }
1304 1362
1305 bool hasExpectedType() => true; 1363 HType get guaranteedType() => foreignType;
1306 } 1364 }
1307 1365
1308 class HForeignNew extends HForeign { 1366 class HForeignNew extends HForeign {
1309 ClassElement element; 1367 ClassElement element;
1310 HForeignNew(this.element, List<HInstruction> inputs) 1368 HForeignNew(this.element, List<HInstruction> inputs)
1311 : super(const LiteralDartString("new"), 1369 : super(const LiteralDartString("new"),
1312 const LiteralDartString("Object"), inputs); 1370 const LiteralDartString("Object"), inputs);
1313 accept(HVisitor visitor) => visitor.visitForeignNew(this); 1371 accept(HVisitor visitor) => visitor.visitForeignNew(this);
1314 } 1372 }
1315 1373
1316 class HInvokeBinary extends HInvokeStatic { 1374 class HInvokeBinary extends HInvokeStatic {
1317 HInvokeBinary(HStatic target, HInstruction left, HInstruction right) 1375 HInvokeBinary(HStatic target, HInstruction left, HInstruction right)
1318 : super(Selector.BINARY_OPERATOR, <HInstruction>[target, left, right]); 1376 : super(Selector.BINARY_OPERATOR, <HInstruction>[target, left, right]);
1319 1377
1320 HInstruction get left() => inputs[1]; 1378 HInstruction get left() => inputs[1];
1321 HInstruction get right() => inputs[2]; 1379 HInstruction get right() => inputs[2];
1322 1380
1323 HType computeInputsType() {
1324 HType leftType = left.type;
1325 HType rightType = right.type;
1326 if (leftType.isUnknown() || rightType.isUnknown()) {
1327 return HType.UNKNOWN;
1328 }
1329 return leftType.combine(rightType);
1330 }
1331
1332 abstract BinaryOperation get operation(); 1381 abstract BinaryOperation get operation();
1333 } 1382 }
1334 1383
1335 class HBinaryArithmetic extends HInvokeBinary { 1384 class HBinaryArithmetic extends HInvokeBinary {
1336 HBinaryArithmetic(HStatic target, HInstruction left, HInstruction right) 1385 HBinaryArithmetic(HStatic target, HInstruction left, HInstruction right)
1337 : super(target, left, right); 1386 : super(target, left, right);
1338 1387
1339 void prepareGvn() { 1388 void prepareGvn() {
1340 // An arithmetic expression can take part in global value 1389 // An arithmetic expression can take part in global value
1341 // numbering and do not have any side-effects if we know that all 1390 // numbering and do not have any side-effects if we know that all
1342 // inputs are numbers. 1391 // inputs are numbers.
1343 if (builtin) { 1392 if (builtin) {
1344 clearAllSideEffects(); 1393 clearAllSideEffects();
1345 setUseGvn(); 1394 setUseGvn();
1346 } else { 1395 } else {
1347 setAllSideEffects(); 1396 setAllSideEffects();
1348 } 1397 }
1349 } 1398 }
1350 1399
1351 bool get builtin() => left.isNumber() && right.isNumber(); 1400 bool get builtin() => left.isNumber() && right.isNumber();
1352 1401
1353 HType computeType() { 1402 HType computeTypeFromInputTypes() {
1354 HType inputsType = computeInputsType(); 1403 if (left.isInteger() && right.isInteger()) return left.propagatedType;
1355 if (inputsType.isKnown()) return inputsType; 1404 if (left.isNumber()) {
1356 if (left.isNumber()) return HType.NUMBER; 1405 if (left.isDouble() || right.isDouble()) return HType.DOUBLE;
1406 return HType.NUMBER;
1407 }
1357 return HType.UNKNOWN; 1408 return HType.UNKNOWN;
1358 } 1409 }
1359 1410
1360 HType computeDesiredInputType(HInstruction input) { 1411 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1361 // TODO(floitsch): we want the target to be a function. 1412 // If the desired output type should be an integer we want to get two
1362 if (input == target) return HType.UNKNOWN; 1413 // integers as arguments.
1363 if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER; 1414 if (propagatedType.isInteger()) return HType.INTEGER;
1364 if (type.isUnknown()) return HType.NUMBER; 1415 // If the outgoing type should be a number we can get that if both inputs
1416 // are numbers. If we don't know the outgoing type we try to make it a
1417 // number.
1418 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1419 return HType.NUMBER;
1420 }
1365 return HType.UNKNOWN; 1421 return HType.UNKNOWN;
1366 } 1422 }
1367 1423
1368 bool hasExpectedType() => left.isNumber() && right.isNumber(); 1424 HType get likelyType() {
1425 if (left.isTypeUnknown()) return HType.NUMBER;
1426 return HType.UNKNOWN;
1427 }
1428
1369 // TODO(1603): The class should be marked as abstract. 1429 // TODO(1603): The class should be marked as abstract.
1370 abstract BinaryOperation get operation(); 1430 abstract BinaryOperation get operation();
1371 } 1431 }
1372 1432
1373 class HAdd extends HBinaryArithmetic { 1433 class HAdd extends HBinaryArithmetic {
1374 HAdd(HStatic target, HInstruction left, HInstruction right) 1434 HAdd(HStatic target, HInstruction left, HInstruction right)
1375 : super(target, left, right); 1435 : super(target, left, right);
1376 accept(HVisitor visitor) => visitor.visitAdd(this); 1436 accept(HVisitor visitor) => visitor.visitAdd(this);
1377 1437
1378 bool get builtin() { 1438 bool get builtin() {
1379 return (left.isNumber() && right.isNumber()) 1439 return (left.isNumber() && right.isNumber())
1380 || (left.isString() && right.isString()) 1440 || (left.isString() && right.isString())
1381 || (left.isString() && right is HConstant); 1441 || (left.isString() && right is HConstant);
1382 } 1442 }
1383 1443
1384 HType computeType() { 1444 HType computeTypeFromInputTypes() {
1385 HType computedType = computeInputsType(); 1445 if (left.isInteger() && right.isInteger()) return left.propagatedType;
1386 if (computedType.isConflicting() && left.isString()) return HType.STRING; 1446 if (left.isNumber()) {
1387 if (computedType.isKnown()) return computedType; 1447 if (left.isDouble() || right.isDouble()) return HType.DOUBLE;
1388 if (left.isNumber()) return HType.NUMBER; 1448 return HType.NUMBER;
1449 }
1450 if (left.isString()) return HType.STRING;
1389 return HType.UNKNOWN; 1451 return HType.UNKNOWN;
1390 } 1452 }
1391 1453
1392 bool hasExpectedType() => builtin || type.isUnknown() || left.isString(); 1454 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1393 1455 // If the desired output type is an integer we want two integers as input.
1394 HType computeDesiredInputType(HInstruction input) { 1456 if (propagatedType.isInteger()) {
1395 // TODO(floitsch): we want the target to be a function. 1457 return HType.INTEGER;
1396 if (input == target) return HType.UNKNOWN;
1397 if (isString() || left.isString()) {
1398 return (input == left) ? HType.STRING : HType.UNKNOWN;
1399 } 1458 }
1400 if (right.isString()) return HType.STRING; 1459 // TODO(floitsch): remove string specialization once string+ is removed
1401 if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER; 1460 // from dart2js.
1461 if (propagatedType.isString() || left.isString() || right.isString()) {
1462 return HType.STRING;
1463 }
1464 // If the desired output is a number or any of the inputs is a number
1465 // ask for a number. Note that we might return the input's (say 'left')
1466 // type depending on its (the 'left's) type. But that shouldn't matter.
1467 if (propagatedType.isNumber() || left.isNumber() || right.isNumber()) {
1468 return HType.NUMBER;
1469 }
1402 return HType.UNKNOWN; 1470 return HType.UNKNOWN;
1403 } 1471 }
1404 1472
1473 HType get likelyType() {
1474 if (left.isString() || right.isString()) return HType.STRING;
1475 if (left.isTypeUnknown() || left.isNumber()) return HType.NUMBER;
1476 return HType.UNKNOWN;
1477 }
1478
1405 AddOperation get operation() => const AddOperation(); 1479 AddOperation get operation() => const AddOperation();
1406 1480
1407 int typeCode() => 5; 1481 int typeCode() => 5;
1408 bool typeEquals(other) => other is HAdd; 1482 bool typeEquals(other) => other is HAdd;
1409 bool dataEquals(HInstruction other) => true; 1483 bool dataEquals(HInstruction other) => true;
1410 } 1484 }
1411 1485
1412 class HDivide extends HBinaryArithmetic { 1486 class HDivide extends HBinaryArithmetic {
1413 HDivide(HStatic target, HInstruction left, HInstruction right) 1487 HDivide(HStatic target, HInstruction left, HInstruction right)
1414 : super(target, left, right); 1488 : super(target, left, right);
1415 accept(HVisitor visitor) => visitor.visitDivide(this); 1489 accept(HVisitor visitor) => visitor.visitDivide(this);
1416 1490
1417 bool get builtin() => left.isNumber() && right.isNumber(); 1491 bool get builtin() => left.isNumber() && right.isNumber();
1418 1492
1419 HType computeType() { 1493 HType computeTypeFromInputTypes() {
1420 HType inputsType = computeInputsType();
1421 if (left.isNumber()) return HType.DOUBLE; 1494 if (left.isNumber()) return HType.DOUBLE;
1422 return HType.UNKNOWN; 1495 return HType.UNKNOWN;
1423 } 1496 }
1424 1497
1498 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1499 // A division can never return an integer. So don't ask for integer inputs.
1500 if (propagatedType.isInteger()) return HType.UNKNOWN;
1501 return super.computeDesiredTypeForNonTargetInput(input);
1502 }
1503
1425 DivideOperation get operation() => const DivideOperation(); 1504 DivideOperation get operation() => const DivideOperation();
1426 int typeCode() => 6; 1505 int typeCode() => 6;
1427 bool typeEquals(other) => other is HDivide; 1506 bool typeEquals(other) => other is HDivide;
1428 bool dataEquals(HInstruction other) => true; 1507 bool dataEquals(HInstruction other) => true;
1429 } 1508 }
1430 1509
1431 class HModulo extends HBinaryArithmetic { 1510 class HModulo extends HBinaryArithmetic {
1432 HModulo(HStatic target, HInstruction left, HInstruction right) 1511 HModulo(HStatic target, HInstruction left, HInstruction right)
1433 : super(target, left, right); 1512 : super(target, left, right);
1434 accept(HVisitor visitor) => visitor.visitModulo(this); 1513 accept(HVisitor visitor) => visitor.visitModulo(this);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1475 1554
1476 1555
1477 // TODO(floitsch): Should HBinaryArithmetic really be the super class of 1556 // TODO(floitsch): Should HBinaryArithmetic really be the super class of
1478 // HBinaryBitOp? 1557 // HBinaryBitOp?
1479 class HBinaryBitOp extends HBinaryArithmetic { 1558 class HBinaryBitOp extends HBinaryArithmetic {
1480 HBinaryBitOp(HStatic target, HInstruction left, HInstruction right) 1559 HBinaryBitOp(HStatic target, HInstruction left, HInstruction right)
1481 : super(target, left, right); 1560 : super(target, left, right);
1482 1561
1483 bool get builtin() => left.isInteger() && right.isInteger(); 1562 bool get builtin() => left.isInteger() && right.isInteger();
1484 1563
1485 HType computeType() { 1564 HType computeTypeFromInputTypes() {
1486 HType inputsType = computeInputsType();
1487 if (inputsType.isKnown()) return inputsType;
1488 if (left.isInteger()) return HType.INTEGER; 1565 if (left.isInteger()) return HType.INTEGER;
1489 return HType.UNKNOWN; 1566 return HType.UNKNOWN;
1490 } 1567 }
1491 1568
1492 HType computeDesiredInputType(HInstruction input) { 1569 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1493 // TODO(floitsch): we want the target to be a function. 1570 // If the outgoing type should be a number we can get that only if both
1494 if (input == target) return HType.UNKNOWN; 1571 // inputs are integers. If we don't know the outgoing type we try to make
1495 return HType.INTEGER; 1572 // it an integer.
1573 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1574 return HType.INTEGER;
1575 }
1576 return HType.UNKNOWN;
1577 }
1578
1579 HType get likelyType() {
1580 if (left.isTypeUnknown()) return HType.INTEGER;
1581 return HType.UNKNOWN;
1496 } 1582 }
1497 1583
1498 // TODO(floitsch): make class abstract instead of adding an abstract method. 1584 // TODO(floitsch): make class abstract instead of adding an abstract method.
1499 abstract accept(HVisitor visitor); 1585 abstract accept(HVisitor visitor);
1500 } 1586 }
1501 1587
1502 class HShiftLeft extends HBinaryBitOp { 1588 class HShiftLeft extends HBinaryBitOp {
1503 HShiftLeft(HStatic target, HInstruction left, HInstruction right) 1589 HShiftLeft(HStatic target, HInstruction left, HInstruction right)
1504 : super(target, left, right); 1590 : super(target, left, right);
1505 accept(HVisitor visitor) => visitor.visitShiftLeft(this); 1591 accept(HVisitor visitor) => visitor.visitShiftLeft(this);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1567 if (builtin) { 1653 if (builtin) {
1568 clearAllSideEffects(); 1654 clearAllSideEffects();
1569 setUseGvn(); 1655 setUseGvn();
1570 } else { 1656 } else {
1571 setAllSideEffects(); 1657 setAllSideEffects();
1572 } 1658 }
1573 } 1659 }
1574 1660
1575 bool get builtin() => operand.isNumber(); 1661 bool get builtin() => operand.isNumber();
1576 1662
1577 HType computeType() { 1663 HType computeTypeFromInputTypes() {
1578 HType operandType = operand.type; 1664 HType operandType = operand.propagatedType;
1579 if (!operandType.isUnknown()) return operandType; 1665 if (operandType.isNumber()) return operandType;
1580 return HType.UNKNOWN; 1666 return HType.UNKNOWN;
1581 } 1667 }
1582 1668
1583 HType computeDesiredInputType(HInstruction input) { 1669 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1584 // TODO(floitsch): we want the target to be a function. 1670 // If the outgoing type should be a number (integer, double or both) we
1585 if (input == target) return HType.UNKNOWN; 1671 // want the outgoing type to be the input too.
1586 if (type.isUnknown() || type.isNumber()) return HType.NUMBER; 1672 // If we don't know the outgoing type we try to make it a number.
1673 if (propagatedType.isNumber()) return propagatedType;
1674 if (propagatedType.isUnknown()) return HType.NUMBER;
1587 return HType.UNKNOWN; 1675 return HType.UNKNOWN;
1588 } 1676 }
1589 1677
1590 bool hasExpectedType() => builtin || type.isUnknown(); 1678 HType get likelyType() => HType.NUMBER;
1591 1679
1592 abstract UnaryOperation get operation(); 1680 abstract UnaryOperation get operation();
1593 } 1681 }
1594 1682
1595 class HNegate extends HInvokeUnary { 1683 class HNegate extends HInvokeUnary {
1596 HNegate(HStatic target, HInstruction input) : super(target, input); 1684 HNegate(HStatic target, HInstruction input) : super(target, input);
1597 accept(HVisitor visitor) => visitor.visitNegate(this); 1685 accept(HVisitor visitor) => visitor.visitNegate(this);
1598 1686
1599 NegateOperation get operation() => const NegateOperation(); 1687 NegateOperation get operation() => const NegateOperation();
1600 int typeCode() => 16; 1688 int typeCode() => 16;
1601 bool typeEquals(other) => other is HNegate; 1689 bool typeEquals(other) => other is HNegate;
1602 bool dataEquals(HInstruction other) => true; 1690 bool dataEquals(HInstruction other) => true;
1603 } 1691 }
1604 1692
1605 class HBitNot extends HInvokeUnary { 1693 class HBitNot extends HInvokeUnary {
1606 HBitNot(HStatic target, HInstruction input) : super(target, input); 1694 HBitNot(HStatic target, HInstruction input) : super(target, input);
1607 accept(HVisitor visitor) => visitor.visitBitNot(this); 1695 accept(HVisitor visitor) => visitor.visitBitNot(this);
1608 1696
1609 bool get builtin() => operand.isInteger(); 1697 bool get builtin() => operand.isInteger();
1610 1698
1611 HType computeType() { 1699 HType computeTypeFromInputTypes() {
1612 HType operandType = operand.type; 1700 HType operandType = operand.propagatedType;
1613 if (!operandType.isUnknown()) return operandType; 1701 if (operandType.isInteger()) return HType.INTEGER;
1614 return HType.UNKNOWN; 1702 return HType.UNKNOWN;
1615 } 1703 }
1616 1704
1617 HType computeDesiredInputType(HInstruction input) { 1705 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1618 // TODO(floitsch): we want the target to be a function. 1706 // Bit operations only work on integers. If there is no desired output
1619 if (input == target) return HType.UNKNOWN; 1707 // type or if it as a number we want to get an integer as input.
1620 return HType.INTEGER; 1708 if (propagatedType.isUnknown() || propagatedType.isNumber()) {
1709 return HType.INTEGER;
1710 }
1711 return HType.UNKNOWN;
1621 } 1712 }
1622 1713
1623 BitNotOperation get operation() => const BitNotOperation(); 1714 BitNotOperation get operation() => const BitNotOperation();
1624 int typeCode() => 17; 1715 int typeCode() => 17;
1625 bool typeEquals(other) => other is HBitNot; 1716 bool typeEquals(other) => other is HBitNot;
1626 bool dataEquals(HInstruction other) => true; 1717 bool dataEquals(HInstruction other) => true;
1627 } 1718 }
1628 1719
1629 class HExit extends HControlFlow { 1720 class HExit extends HControlFlow {
1630 HExit() : super(const <HInstruction>[]); 1721 HExit() : super(const <HInstruction>[]);
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1699 toString() => 'loop-branch'; 1790 toString() => 'loop-branch';
1700 accept(HVisitor visitor) => visitor.visitLoopBranch(this); 1791 accept(HVisitor visitor) => visitor.visitLoopBranch(this);
1701 1792
1702 bool isDoWhile() { 1793 bool isDoWhile() {
1703 return kind === DO_WHILE_LOOP; 1794 return kind === DO_WHILE_LOOP;
1704 } 1795 }
1705 } 1796 }
1706 1797
1707 class HConstant extends HInstruction { 1798 class HConstant extends HInstruction {
1708 final Constant constant; 1799 final Constant constant;
1709 HConstant.internal(this.constant, HType type) : super(<HInstruction>[]) { 1800 final HType constantType;
1710 this.type = type; 1801 HConstant.internal(this.constant, HType this.constantType)
1711 } 1802 : super(<HInstruction>[]);
1712 1803
1713 void prepareGvn() { 1804 void prepareGvn() {
1714 assert(!hasSideEffects()); 1805 assert(!hasSideEffects());
1715 } 1806 }
1716 1807
1717 toString() => 'literal: $constant'; 1808 toString() => 'literal: $constant';
1718 accept(HVisitor visitor) => visitor.visitConstant(this); 1809 accept(HVisitor visitor) => visitor.visitConstant(this);
1719 HType computeType() => type;
1720 1810
1721 bool hasExpectedType() => true; 1811 HType get guaranteedType() => constantType;
1722 1812
1723 bool isConstant() => true; 1813 bool isConstant() => true;
1724 bool isConstantBoolean() => constant.isBool(); 1814 bool isConstantBoolean() => constant.isBool();
1725 bool isConstantNull() => constant.isNull(); 1815 bool isConstantNull() => constant.isNull();
1726 bool isConstantNumber() => constant.isNum(); 1816 bool isConstantNumber() => constant.isNum();
1727 bool isConstantString() => constant.isString(); 1817 bool isConstantString() => constant.isString();
1728 1818
1729 // Maybe avoid this if the literal is big? 1819 // Maybe avoid this if the literal is big?
1730 bool isCodeMotionInvariant() => true; 1820 bool isCodeMotionInvariant() => true;
1731 } 1821 }
1732 1822
1733 class HNot extends HInstruction { 1823 class HNot extends HInstruction {
1734 HNot(HInstruction value) : super(<HInstruction>[value]); 1824 HNot(HInstruction value) : super(<HInstruction>[value]);
1735 void prepareGvn() { 1825 void prepareGvn() {
1736 assert(!hasSideEffects()); 1826 assert(!hasSideEffects());
1737 setUseGvn(); 1827 setUseGvn();
1738 } 1828 }
1739 1829
1740 HType computeType() => HType.BOOLEAN; 1830 HType get guaranteedType() => HType.BOOLEAN;
1741 bool hasExpectedType() => true; 1831
1742 HType computeDesiredInputType(HInstruction input) { 1832 // 'Not' only works on booleans. That's what we want as input.
1743 return HType.BOOLEAN; 1833 HType computeDesiredTypeForInput(HInstruction input) => HType.BOOLEAN;
1744 }
1745 1834
1746 accept(HVisitor visitor) => visitor.visitNot(this); 1835 accept(HVisitor visitor) => visitor.visitNot(this);
1747 int typeCode() => 18; 1836 int typeCode() => 18;
1748 bool typeEquals(other) => other is HNot; 1837 bool typeEquals(other) => other is HNot;
1749 bool dataEquals(HInstruction other) => true; 1838 bool dataEquals(HInstruction other) => true;
1750 } 1839 }
1751 1840
1752 class HParameterValue extends HInstruction { 1841 class HParameterValue extends HInstruction {
1753 final Element element; 1842 final Element element;
1754 1843
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1790 void addInput(HInstruction input) { 1879 void addInput(HInstruction input) {
1791 assert(isInBasicBlock()); 1880 assert(isInBasicBlock());
1792 inputs.add(input); 1881 inputs.add(input);
1793 input.usedBy.add(this); 1882 input.usedBy.add(this);
1794 } 1883 }
1795 1884
1796 // Compute the (shared) type of the inputs if any. If all inputs 1885 // Compute the (shared) type of the inputs if any. If all inputs
1797 // have the same known type return it. If any two inputs have 1886 // have the same known type return it. If any two inputs have
1798 // different known types, we'll return a conflict -- otherwise we'll 1887 // different known types, we'll return a conflict -- otherwise we'll
1799 // simply return an unknown type. 1888 // simply return an unknown type.
1800 HType computeInputsType() { 1889 HType computeInputsType(bool unknownWins) {
1801 bool seenUnknown = false; 1890 bool seenUnknown = false;
1802 HType candidateType = inputs[0].type; 1891 HType candidateType = inputs[0].propagatedType;
1803 for (int i = 1, length = inputs.length; i < length; i++) { 1892 for (int i = 1, length = inputs.length; i < length; i++) {
1804 HType inputType = inputs[i].type; 1893 HType inputType = inputs[i].propagatedType;
1805 if (inputType.isUnknown()) return HType.UNKNOWN; 1894 if (inputType.isUnknown()) {
1806 candidateType = candidateType.combine(inputType); 1895 seenUnknown = true;
1807 if (candidateType.isConflicting()) return HType.CONFLICTING; 1896 } else {
1897 candidateType = candidateType.combine(inputType);
1898 if (candidateType.isConflicting()) return HType.CONFLICTING;
1899 }
1808 } 1900 }
1901 if (seenUnknown && unknownWins) return HType.UNKNOWN;
1809 return candidateType; 1902 return candidateType;
1810 } 1903 }
1811 1904
1812 HType computeType() { 1905 HType computeTypeFromInputTypes() {
1813 HType inputsType = computeInputsType(); 1906 HType inputsType = computeInputsType(true);
1814 if (!inputsType.isUnknown()) return inputsType; 1907 if (inputsType.isConflicting()) return HType.UNKNOWN;
1815 return super.computeType(); 1908 return inputsType;
1816 } 1909 }
1817 1910
1818 HType computeDesiredInputType(HInstruction input) { 1911 HType computeDesiredTypeForInput(HInstruction input) {
1819 if (type.isNumber()) return HType.NUMBER; 1912 // Best case scenario for a phi is, when all inputs have the same type. If
1820 if (type.isStringOrArray()) return HType.STRING_OR_ARRAY; 1913 // there is no desired outgoing type we therefore try to unify the input
1821 return type; 1914 // types (which is basically the [likelyType]).
1915 if (propagatedType.isUnknown()) return likelyType;
1916 // When the desired outgoing type is conflicting we don't need to give any
1917 // requirements on the inputs.
1918 if (propagatedType.isConflicting()) return HType.UNKNOWN;
1919 // Otherwise the input type must match the desired outgoing type.
1920 return propagatedType;
1822 } 1921 }
1823 1922
1824 bool hasExpectedType() { 1923 HType get likelyType() {
1825 for (int i = 0; i < inputs.length; i++) { 1924 HType agreedType = computeInputsType(false);
1826 if (type.combine(inputs[i].type).isConflicting()) return false; 1925 if (agreedType.isConflicting()) return HType.UNKNOWN;
1827 } 1926 // Don't be too restrictive. If the agreed type is integer or double just
1828 return true; 1927 // say that the likely type is number. If more is expected the type will be
1928 // propagated back.
1929 if (agreedType.isNumber()) return HType.NUMBER;
1930 return agreedType;
1829 } 1931 }
1830 1932
1831 bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR; 1933 bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
1832 1934
1833 String logicalOperator() { 1935 String logicalOperator() {
1834 assert(isLogicalOperator()); 1936 assert(isLogicalOperator());
1835 if (logicalOperatorType == IS_AND) return "&&"; 1937 if (logicalOperatorType == IS_AND) return "&&";
1836 assert(logicalOperatorType == IS_OR); 1938 assert(logicalOperatorType == IS_OR);
1837 return "||"; 1939 return "||";
1838 } 1940 }
1839 1941
1840 toString() => 'phi'; 1942 toString() => 'phi';
1841 accept(HVisitor visitor) => visitor.visitPhi(this); 1943 accept(HVisitor visitor) => visitor.visitPhi(this);
1842 } 1944 }
1843 1945
1844 class HRelational extends HInvokeBinary { 1946 class HRelational extends HInvokeBinary {
1845 HRelational(HStatic target, HInstruction left, HInstruction right) 1947 HRelational(HStatic target, HInstruction left, HInstruction right)
1846 : super(target, left, right) { 1948 : super(target, left, right);
1847 type = HType.BOOLEAN;
1848 }
1849 1949
1850 void prepareGvn() { 1950 void prepareGvn() {
1851 // Relational expressions can take part in global value numbering 1951 // Relational expressions can take part in global value numbering
1852 // and do not have any side-effects if we know all the inputs are 1952 // and do not have any side-effects if we know all the inputs are
1853 // numbers. This can be improved for at least equality. 1953 // numbers. This can be improved for at least equality.
1854 if (builtin) { 1954 if (builtin) {
1855 clearAllSideEffects(); 1955 clearAllSideEffects();
1856 setUseGvn(); 1956 setUseGvn();
1857 } else { 1957 } else {
1858 setAllSideEffects(); 1958 setAllSideEffects();
1859 } 1959 }
1860 } 1960 }
1861 1961
1862 HType computeDesiredInputType(HInstruction input) { 1962 HType computeTypeFromInputTypes() {
1863 // TODO(floitsch): we want the target to be a function. 1963 if (left.isNumber()) return HType.BOOLEAN;
1864 if (input == target) return HType.UNKNOWN; 1964 return HType.UNKNOWN;
1865 // For all relational operations exept HEquals, we expect to only
1866 // get numbers.
1867 return HType.NUMBER;
1868 } 1965 }
1869 1966
1967 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
1968 // For all relational operations exept HEquals, we expect to get numbers
1969 // only. With numbers the outgoing type is a boolean. If something else
1970 // is desired, then numbers are incorrect, though.
1971 if (propagatedType.isUnknown() || propagatedType.isBoolean()) {
1972 if (left.isTypeUnknown() || left.isNumber()) return HType.NUMBER;
1973 }
1974 return HType.UNKNOWN;
1975 }
1976
1977 HType get likelyType() => HType.BOOLEAN;
1978
1870 bool get builtin() => left.isNumber() && right.isNumber(); 1979 bool get builtin() => left.isNumber() && right.isNumber();
1871 HType computeType() => HType.BOOLEAN;
1872 // A HRelational goes through the builtin operator or the top level
1873 // element. Therefore, it always has the expected type.
1874 bool hasExpectedType() => true;
1875 // TODO(1603): the class should be marked as abstract. 1980 // TODO(1603): the class should be marked as abstract.
1876 abstract BinaryOperation get operation(); 1981 abstract BinaryOperation get operation();
1877 } 1982 }
1878 1983
1879 class HEquals extends HRelational { 1984 class HEquals extends HRelational {
1880 HEquals(HStatic target, HInstruction left, HInstruction right) 1985 HEquals(HStatic target, HInstruction left, HInstruction right)
1881 : super(target, left, right); 1986 : super(target, left, right);
1882 accept(HVisitor visitor) => visitor.visitEquals(this); 1987 accept(HVisitor visitor) => visitor.visitEquals(this);
1883 1988
1884 bool get builtin() { 1989 bool get builtin() {
1885 if (left.isNumber() && right.isNumber()) return true; 1990 // All useful types have === semantics.
1886 if (left is !HConstant) return false; 1991 // Note that this includes all constants except the user-constructed
1887 HConstant leftConstant = left; 1992 // objects.
1888 // TODO(floitsch): we can do better if we know that the constant does not 1993 return left.isConstantNull() || left.propagatedType.isUseful();
1889 // have the equality operator overridden.
1890 return !leftConstant.constant.isConstructedObject();
1891 } 1994 }
1892 1995
1893 HType computeType() => HType.BOOLEAN; 1996 HType computeTypeFromInputTypes() {
1894 1997 if (builtin) return HType.BOOLEAN;
1895 HType computeDesiredInputType(HInstruction input) {
1896 // TODO(floitsch): we want the target to be a function.
1897 if (input == target) return HType.UNKNOWN;
1898 if (left.isNumber() || right.isNumber()) return HType.NUMBER;
1899 return HType.UNKNOWN; 1998 return HType.UNKNOWN;
1900 } 1999 }
1901 2000
2001 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
2002 if (input == left && right.propagatedType.isUseful()) {
2003 // All our useful types have === semantics. But we don't want to
2004 // speculatively test for all possible types. Therefore we try to match
2005 // the two types. That is, if we see x == 3, then we speculatively test
2006 // if x is a number and bailout if it isn't.
2007 if (right.isNumber()) return HType.NUMBER; // No need to be more precise.
2008 // String equality testing is much more common than array equality
2009 // testing.
2010 if (right.isStringOrArray()) return HType.STRING;
2011 return right.propagatedType;
2012 }
2013 // String equality testing is much more common than array equality testing.
2014 if (input == left && left.isStringOrArray()) {
2015 return HType.READABLE_ARRAY;
2016 }
2017 // String equality testing is much more common than array equality testing.
2018 if (input == right && right.isStringOrArray()) {
2019 return HType.STRING;
2020 }
2021 return HType.UNKNOWN;
2022 }
2023
1902 EqualsOperation get operation() => const EqualsOperation(); 2024 EqualsOperation get operation() => const EqualsOperation();
1903 int typeCode() => 19; 2025 int typeCode() => 19;
1904 bool typeEquals(other) => other is HEquals; 2026 bool typeEquals(other) => other is HEquals;
1905 bool dataEquals(HInstruction other) => true; 2027 bool dataEquals(HInstruction other) => true;
1906 } 2028 }
1907 2029
1908 class HIdentity extends HRelational { 2030 class HIdentity extends HRelational {
1909 HIdentity(HStatic target, HInstruction left, HInstruction right) 2031 HIdentity(HStatic target, HInstruction left, HInstruction right)
1910 : super(target, left, right); 2032 : super(target, left, right);
1911 accept(HVisitor visitor) => visitor.visitIdentity(this); 2033 accept(HVisitor visitor) => visitor.visitIdentity(this);
1912 2034
1913 bool get builtin() => true; 2035 bool get builtin() => true;
1914 HType computeType() => HType.BOOLEAN;
1915 bool hasExpectedType() => true;
1916 2036
1917 HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN; 2037 HType get guaranteedType() => HType.BOOLEAN;
2038 HType computeTypeFromInputTypes() => HType.BOOLEAN;
2039 // Note that the identity operator really does not care for its input types.
2040 HType computeDesiredTypeForInput(HInstruction input) => HType.UNKNOWN;
1918 2041
1919 IdentityOperation get operation() => const IdentityOperation(); 2042 IdentityOperation get operation() => const IdentityOperation();
1920 int typeCode() => 20; 2043 int typeCode() => 20;
1921 bool typeEquals(other) => other is HIdentity; 2044 bool typeEquals(other) => other is HIdentity;
1922 bool dataEquals(HInstruction other) => true; 2045 bool dataEquals(HInstruction other) => true;
1923 } 2046 }
1924 2047
1925 class HGreater extends HRelational { 2048 class HGreater extends HRelational {
1926 HGreater(HStatic target, HInstruction left, HInstruction right) 2049 HGreater(HStatic target, HInstruction left, HInstruction right)
1927 : super(target, left, right); 2050 : super(target, left, right);
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
2007 2130
2008 int typeCode() => 26; 2131 int typeCode() => 26;
2009 bool typeEquals(other) => other is HStaticStore; 2132 bool typeEquals(other) => other is HStaticStore;
2010 bool dataEquals(HStaticStore other) => element == other.element; 2133 bool dataEquals(HStaticStore other) => element == other.element;
2011 } 2134 }
2012 2135
2013 class HLiteralList extends HInstruction { 2136 class HLiteralList extends HInstruction {
2014 HLiteralList(inputs) : super(inputs); 2137 HLiteralList(inputs) : super(inputs);
2015 toString() => 'literal list'; 2138 toString() => 'literal list';
2016 accept(HVisitor visitor) => visitor.visitLiteralList(this); 2139 accept(HVisitor visitor) => visitor.visitLiteralList(this);
2017 HType computeType() => HType.MUTABLE_ARRAY; 2140
2018 bool hasExpectedType() => true; 2141 HType get guaranteedType() => HType.MUTABLE_ARRAY;
2019 2142
2020 void prepareGvn() { 2143 void prepareGvn() {
2021 assert(!hasSideEffects()); 2144 assert(!hasSideEffects());
2022 } 2145 }
2023 } 2146 }
2024 2147
2025 class HIndex extends HInvokeStatic { 2148 class HIndex extends HInvokeStatic {
2026 HIndex(HStatic target, HInstruction receiver, HInstruction index) 2149 HIndex(HStatic target, HInstruction receiver, HInstruction index)
2027 : super(Selector.INDEX, <HInstruction>[target, receiver, index]); 2150 : super(Selector.INDEX, <HInstruction>[target, receiver, index]);
2028 toString() => 'index operator'; 2151 toString() => 'index operator';
2029 accept(HVisitor visitor) => visitor.visitIndex(this); 2152 accept(HVisitor visitor) => visitor.visitIndex(this);
2030 2153
2031 void prepareGvn() { 2154 void prepareGvn() {
2032 if (builtin) { 2155 if (builtin) {
2033 clearAllSideEffects(); 2156 clearAllSideEffects();
2034 } else { 2157 } else {
2035 setAllSideEffects(); 2158 setAllSideEffects();
2036 } 2159 }
2037 } 2160 }
2038 2161
2039 HInstruction get receiver() => inputs[1]; 2162 HInstruction get receiver() => inputs[1];
2040 HInstruction get index() => inputs[2]; 2163 HInstruction get index() => inputs[2];
2041 2164
2042 HType computeDesiredInputType(HInstruction input) { 2165 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
2043 // TODO(floitsch): we want the target to be a function. 2166 if (input == receiver && (index.isTypeUnknown() || index.isNumber())) {
2044 if (input == target) return HType.UNKNOWN; 2167 return HType.STRING_OR_ARRAY;
2045 if (input == receiver) return HType.STRING_OR_ARRAY; 2168 }
2169 // The index should be an int when the receiver is a string or array.
2170 // However it turns out that inserting an integer check in the optimized
2171 // version is cheaper than having another bailout case. This is true,
2172 // because the integer check will simply throw if it fails.
2046 return HType.UNKNOWN; 2173 return HType.UNKNOWN;
2047 } 2174 }
2048 2175
2049 bool get builtin() => receiver.isStringOrArray(); 2176 bool get builtin() => receiver.isStringOrArray() && index.isInteger();
2050 HType computeType() => HType.UNKNOWN;
2051 bool hasExpectedType() => false;
2052 } 2177 }
2053 2178
2054 class HIndexAssign extends HInvokeStatic { 2179 class HIndexAssign extends HInvokeStatic {
2055 HIndexAssign(HStatic target, 2180 HIndexAssign(HStatic target,
2056 HInstruction receiver, 2181 HInstruction receiver,
2057 HInstruction index, 2182 HInstruction index,
2058 HInstruction value) 2183 HInstruction value)
2059 : super(Selector.INDEX_SET, 2184 : super(Selector.INDEX_SET,
2060 <HInstruction>[target, receiver, index, value]); 2185 <HInstruction>[target, receiver, index, value]);
2061 toString() => 'index assign operator'; 2186 toString() => 'index assign operator';
2062 accept(HVisitor visitor) => visitor.visitIndexAssign(this); 2187 accept(HVisitor visitor) => visitor.visitIndexAssign(this);
2063 2188
2064 HInstruction get receiver() => inputs[1]; 2189 HInstruction get receiver() => inputs[1];
2065 HInstruction get index() => inputs[2]; 2190 HInstruction get index() => inputs[2];
2066 HInstruction get value() => inputs[3]; 2191 HInstruction get value() => inputs[3];
2067 2192
2068 HType computeDesiredInputType(HInstruction input) { 2193 // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign]
2069 // TODO(floitsch): we want the target to be a function. 2194 // is never used as input.
2070 if (input == target) return HType.UNKNOWN; 2195
2071 if (input == receiver) return HType.MUTABLE_ARRAY; 2196 HType computeDesiredTypeForNonTargetInput(HInstruction input) {
2197 if (input == receiver && (index.isTypeUnknown() || index.isNumber())) {
2198 return HType.MUTABLE_ARRAY;
2199 }
2200 // The index should be an int when the receiver is a string or array.
2201 // However it turns out that inserting an integer check in the optimized
2202 // version is cheaper than having another bailout case. This is true,
2203 // because the integer check will simply throw if it fails.
2072 return HType.UNKNOWN; 2204 return HType.UNKNOWN;
2073 } 2205 }
2074 2206
2075 bool get builtin() => receiver.isMutableArray(); 2207 bool get builtin() => receiver.isMutableArray() && index.isInteger();
2076 HType computeType() => value.type;
2077 // This instruction does not yield a new value, so it always
2078 // has the expected type (void).
2079 bool hasExpectedType() => true;
2080 } 2208 }
2081 2209
2082 class HIs extends HInstruction { 2210 class HIs extends HInstruction {
2083 final Type typeName; 2211 final Type typeName;
2084 final bool nullOk; 2212 final bool nullOk;
2085 2213
2086 HIs(this.typeName, HInstruction expression, [nullOk = false]) 2214 HIs(this.typeName, HInstruction expression, [nullOk = false])
2087 : this.nullOk = nullOk, super(<HInstruction>[expression]); 2215 : this.nullOk = nullOk, super(<HInstruction>[expression]);
2088 2216
2089 HInstruction get expression() => inputs[0]; 2217 HInstruction get expression() => inputs[0];
2090 2218
2091 HType computeType() => HType.BOOLEAN; 2219 HType get guaranteedType() => HType.BOOLEAN;
2092 bool hasExpectedType() => true;
2093 2220
2094 accept(HVisitor visitor) => visitor.visitIs(this); 2221 accept(HVisitor visitor) => visitor.visitIs(this);
2095 2222
2096 toString() => "$expression is $typeName"; 2223 toString() => "$expression is $typeName";
2097 } 2224 }
2098 2225
2099 class HIfBlockInformation { 2226 class HIfBlockInformation {
2100 final HIf branch; 2227 final HIf branch;
2101 final SubGraph thenGraph; 2228 final SubGraph thenGraph;
2102 final SubGraph elseGraph; 2229 final SubGraph elseGraph;
2103 final HBasicBlock joinBlock; 2230 final HBasicBlock joinBlock;
2104 HIfBlockInformation(this.branch, 2231 HIfBlockInformation(this.branch,
2105 this.thenGraph, 2232 this.thenGraph,
2106 this.elseGraph, 2233 this.elseGraph,
2107 this.joinBlock); 2234 this.joinBlock);
2108 } 2235 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/ssa/bailout.dart ('k') | lib/compiler/implementation/ssa/optimize.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698