| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 library dart2js.cps_ir.type_propagation; | 4 library dart2js.cps_ir.type_propagation; |
| 5 | 5 |
| 6 import 'optimizers.dart'; | 6 import 'optimizers.dart'; |
| 7 | 7 |
| 8 import '../closure.dart' show | 8 import '../closure.dart' show |
| 9 ClosureClassElement; | 9 ClosureClassElement; |
| 10 import '../common.dart'; | 10 import '../common.dart'; |
| (...skipping 1142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1153 return compiler.world.allFunctions.filter(selector, receiverType); | 1153 return compiler.world.allFunctions.filter(selector, receiverType); |
| 1154 } | 1154 } |
| 1155 | 1155 |
| 1156 /************************* CALL EXPRESSIONS *************************/ | 1156 /************************* CALL EXPRESSIONS *************************/ |
| 1157 | 1157 |
| 1158 /// Replaces [node] with a more specialized instruction, if possible. | 1158 /// Replaces [node] with a more specialized instruction, if possible. |
| 1159 /// | 1159 /// |
| 1160 /// Returns `true` if the node was replaced. | 1160 /// Returns `true` if the node was replaced. |
| 1161 specializeOperatorCall(InvokeMethod node) { | 1161 specializeOperatorCall(InvokeMethod node) { |
| 1162 if (!backend.isInterceptedSelector(node.selector)) return null; | 1162 if (!backend.isInterceptedSelector(node.selector)) return null; |
| 1163 if (node.dartArgumentsLength > 1) return null; | 1163 if (node.argumentRefs.length > 1) return null; |
| 1164 if (node.callingConvention == CallingConvention.OneShotIntercepted) { | 1164 if (node.callingConvention == CallingConvention.OneShotIntercepted) { |
| 1165 return null; | 1165 return null; |
| 1166 } | 1166 } |
| 1167 | 1167 |
| 1168 bool trustPrimitives = compiler.trustPrimitives; | 1168 bool trustPrimitives = compiler.trustPrimitives; |
| 1169 | 1169 |
| 1170 /// Check that the receiver and argument satisfy the given type checks, and | 1170 /// Check that the receiver and argument satisfy the given type checks, and |
| 1171 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. | 1171 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. |
| 1172 CpsFragment makeGuard(TypeCheckOperator receiverGuard, | 1172 CpsFragment makeGuard(TypeCheckOperator receiverGuard, |
| 1173 [TypeCheckOperator argumentGuard]) { | 1173 [TypeCheckOperator argumentGuard]) { |
| 1174 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1174 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1175 | 1175 |
| 1176 // Make no guards if trusting primitives. | 1176 // Make no guards if trusting primitives. |
| 1177 if (trustPrimitives) return cps; | 1177 if (trustPrimitives) return cps; |
| 1178 | 1178 |
| 1179 // Determine which guards are needed. | 1179 // Determine which guards are needed. |
| 1180 ChecksNeeded receiverChecks = | 1180 ChecksNeeded receiverChecks = |
| 1181 receiverGuard.getChecksNeeded(node.dartReceiver, classWorld); | 1181 receiverGuard.getChecksNeeded(node.receiver, classWorld); |
| 1182 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; | 1182 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; |
| 1183 bool needArgumentGuard = | 1183 bool needArgumentGuard = |
| 1184 argumentGuard != null && | 1184 argumentGuard != null && |
| 1185 argumentGuard.needsCheck(node.dartArgument(0), classWorld); | 1185 argumentGuard.needsCheck(node.argument(0), classWorld); |
| 1186 | 1186 |
| 1187 if (!needReceiverGuard && !needArgumentGuard) return cps; | 1187 if (!needReceiverGuard && !needArgumentGuard) return cps; |
| 1188 | 1188 |
| 1189 // If we only need the receiver check, emit the specialized receiver | 1189 // If we only need the receiver check, emit the specialized receiver |
| 1190 // check instruction. Examples: | 1190 // check instruction. Examples: |
| 1191 // | 1191 // |
| 1192 // if (typeof receiver !== "number") return receiver.$lt; | 1192 // if (typeof receiver !== "number") return receiver.$lt; |
| 1193 // if (typeof receiver !== "number") return receiver.$lt(); | 1193 // if (typeof receiver !== "number") return receiver.$lt(); |
| 1194 // | 1194 // |
| 1195 if (!needArgumentGuard) { | 1195 if (!needArgumentGuard) { |
| 1196 Primitive condition = receiverGuard.makeCheck(cps, node.dartReceiver); | 1196 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); |
| 1197 cps.letPrim(new ReceiverCheck( | 1197 cps.letPrim(new ReceiverCheck( |
| 1198 node.dartReceiver, | 1198 node.receiver, |
| 1199 node.selector, | 1199 node.selector, |
| 1200 node.sourceInformation, | 1200 node.sourceInformation, |
| 1201 condition: condition, | 1201 condition: condition, |
| 1202 useSelector: true, | 1202 useSelector: true, |
| 1203 isNullCheck: receiverChecks == ChecksNeeded.Null | 1203 isNullCheck: receiverChecks == ChecksNeeded.Null |
| 1204 )); | 1204 )); |
| 1205 return cps; | 1205 return cps; |
| 1206 } | 1206 } |
| 1207 | 1207 |
| 1208 // TODO(asgerf): We should consider specialized instructions for | 1208 // TODO(asgerf): We should consider specialized instructions for |
| 1209 // argument checks and receiver+argument checks, to avoid breaking up | 1209 // argument checks and receiver+argument checks, to avoid breaking up |
| 1210 // basic blocks. | 1210 // basic blocks. |
| 1211 | 1211 |
| 1212 // Emit as `H.iae(x)` if only the argument check may fail. For example: | 1212 // Emit as `H.iae(x)` if only the argument check may fail. For example: |
| 1213 // | 1213 // |
| 1214 // if (typeof argument !== "number") return H.iae(argument); | 1214 // if (typeof argument !== "number") return H.iae(argument); |
| 1215 // | 1215 // |
| 1216 if (!needReceiverGuard) { | 1216 if (!needReceiverGuard) { |
| 1217 cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) | 1217 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
| 1218 .invokeStaticThrower(helpers.throwIllegalArgumentException, | 1218 .invokeStaticThrower(helpers.throwIllegalArgumentException, |
| 1219 [node.dartArgument(0)]); | 1219 [node.argument(0)]); |
| 1220 return cps; | 1220 return cps; |
| 1221 } | 1221 } |
| 1222 | 1222 |
| 1223 // Both receiver and argument check is needed. Emit as a combined check | 1223 // Both receiver and argument check is needed. Emit as a combined check |
| 1224 // using a one-shot interceptor to produce the exact error message in | 1224 // using a one-shot interceptor to produce the exact error message in |
| 1225 // the error case. For example: | 1225 // the error case. For example: |
| 1226 // | 1226 // |
| 1227 // if (typeof receiver !== "number" || typeof argument !== "number") | 1227 // if (typeof receiver !== "number" || typeof argument !== "number") |
| 1228 // return J.$lt(receiver, argument); | 1228 // return J.$lt(receiver, argument); |
| 1229 // | 1229 // |
| 1230 Continuation fail = cps.letCont(); | 1230 Continuation fail = cps.letCont(); |
| 1231 cps.ifTruthy(receiverGuard.makeCheck(cps, node.dartReceiver)) | 1231 cps.ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) |
| 1232 .invokeContinuation(fail); | 1232 .invokeContinuation(fail); |
| 1233 cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) | 1233 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
| 1234 .invokeContinuation(fail); | 1234 .invokeContinuation(fail); |
| 1235 | 1235 |
| 1236 cps.insideContinuation(fail) | 1236 cps.insideContinuation(fail) |
| 1237 ..invokeMethod(node.dartReceiver, node.selector, node.mask, | 1237 ..invokeMethod(node.receiver, node.selector, node.mask, |
| 1238 [node.dartArgument(0)], CallingConvention.OneShotIntercepted) | 1238 [node.argument(0)], |
| 1239 callingConvention: CallingConvention.OneShotIntercepted) |
| 1239 ..put(new Unreachable()); | 1240 ..put(new Unreachable()); |
| 1240 | 1241 |
| 1241 return cps; | 1242 return cps; |
| 1242 } | 1243 } |
| 1243 | 1244 |
| 1244 /// Replaces the call with [operator], using the receiver and first argument | 1245 /// Replaces the call with [operator], using the receiver and first argument |
| 1245 /// as operands (in that order). | 1246 /// as operands (in that order). |
| 1246 /// | 1247 /// |
| 1247 /// If [guard] is given, the receiver and argument are both checked using | 1248 /// If [guard] is given, the receiver and argument are both checked using |
| 1248 /// that operator. | 1249 /// that operator. |
| 1249 CpsFragment makeBinary(BuiltinOperator operator, | 1250 CpsFragment makeBinary(BuiltinOperator operator, |
| 1250 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1251 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
| 1251 CpsFragment cps = makeGuard(guard, guard); | 1252 CpsFragment cps = makeGuard(guard, guard); |
| 1252 Primitive left = guard.makeRefinement(cps, node.dartReceiver, classWorld); | 1253 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); |
| 1253 Primitive right = | 1254 Primitive right = |
| 1254 guard.makeRefinement(cps, node.dartArgument(0), classWorld); | 1255 guard.makeRefinement(cps, node.argument(0), classWorld); |
| 1255 Primitive result = cps.applyBuiltin(operator, [left, right]); | 1256 Primitive result = cps.applyBuiltin(operator, [left, right]); |
| 1256 result.hint = node.hint; | 1257 result.hint = node.hint; |
| 1257 node.replaceUsesWith(result); | 1258 node.replaceUsesWith(result); |
| 1258 return cps; | 1259 return cps; |
| 1259 } | 1260 } |
| 1260 | 1261 |
| 1261 /// Like [makeBinary] but for unary operators with the receiver as the | 1262 /// Like [makeBinary] but for unary operators with the receiver as the |
| 1262 /// argument. | 1263 /// argument. |
| 1263 CpsFragment makeUnary(BuiltinOperator operator, | 1264 CpsFragment makeUnary(BuiltinOperator operator, |
| 1264 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1265 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
| 1265 CpsFragment cps = makeGuard(guard); | 1266 CpsFragment cps = makeGuard(guard); |
| 1266 Primitive argument = | 1267 Primitive argument = |
| 1267 guard.makeRefinement(cps, node.dartReceiver, classWorld); | 1268 guard.makeRefinement(cps, node.receiver, classWorld); |
| 1268 Primitive result = cps.applyBuiltin(operator, [argument]); | 1269 Primitive result = cps.applyBuiltin(operator, [argument]); |
| 1269 result.hint = node.hint; | 1270 result.hint = node.hint; |
| 1270 node.replaceUsesWith(result); | 1271 node.replaceUsesWith(result); |
| 1271 return cps; | 1272 return cps; |
| 1272 } | 1273 } |
| 1273 | 1274 |
| 1274 Selector renameToOptimizedSelector(String name) { | 1275 Selector renameToOptimizedSelector(String name) { |
| 1275 return new Selector.call( | 1276 return new Selector.call( |
| 1276 new Name(name, backend.helpers.interceptorsLibrary), | 1277 new Name(name, backend.helpers.interceptorsLibrary), |
| 1277 node.selector.callStructure); | 1278 node.selector.callStructure); |
| 1278 } | 1279 } |
| 1279 | 1280 |
| 1280 /// Replaces the call with a call to [name] with the same inputs. | 1281 /// Replaces the call with a call to [name] with the same inputs. |
| 1281 InvokeMethod makeRenamedInvoke(String name) { | 1282 InvokeMethod makeRenamedInvoke(String name) { |
| 1282 return new InvokeMethod(node.receiver, | 1283 return new InvokeMethod(node.receiver, |
| 1283 renameToOptimizedSelector(name), | 1284 renameToOptimizedSelector(name), |
| 1284 node.mask, | 1285 node.mask, |
| 1285 node.arguments.toList(), | 1286 node.arguments.toList(), |
| 1286 sourceInformation: node.sourceInformation, | 1287 sourceInformation: node.sourceInformation, |
| 1287 callingConvention: node.callingConvention); | 1288 callingConvention: node.callingConvention, |
| 1289 interceptor: node.interceptor); |
| 1288 } | 1290 } |
| 1289 | 1291 |
| 1290 TypeMask successType = | 1292 TypeMask successType = |
| 1291 typeSystem.receiverTypeFor(node.selector, node.dartReceiver.type); | 1293 typeSystem.receiverTypeFor(node.selector, node.receiver.type); |
| 1292 | 1294 |
| 1293 if (node.selector.isOperator && node.dartArgumentsLength == 1) { | 1295 if (node.selector.isOperator && node.argumentRefs.length == 1) { |
| 1294 Primitive leftArg = node.dartReceiver; | 1296 Primitive leftArg = node.receiver; |
| 1295 Primitive rightArg = node.dartArgument(0); | 1297 Primitive rightArg = node.argument(0); |
| 1296 AbstractConstantValue left = getValue(leftArg); | 1298 AbstractConstantValue left = getValue(leftArg); |
| 1297 AbstractConstantValue right = getValue(rightArg); | 1299 AbstractConstantValue right = getValue(rightArg); |
| 1298 | 1300 |
| 1299 String opname = node.selector.name; | 1301 String opname = node.selector.name; |
| 1300 if (opname == '==') { | 1302 if (opname == '==') { |
| 1301 // Equality is special due to its treatment of null values and the | 1303 // Equality is special due to its treatment of null values and the |
| 1302 // fact that Dart-null corresponds to both JS-null and JS-undefined. | 1304 // fact that Dart-null corresponds to both JS-null and JS-undefined. |
| 1303 // Please see documentation for IsFalsy, StrictEq, and LooseEq. | 1305 // Please see documentation for IsFalsy, StrictEq, and LooseEq. |
| 1304 if (left.isNullConstant || right.isNullConstant) { | 1306 if (left.isNullConstant || right.isNullConstant) { |
| 1305 return makeBinary(BuiltinOperator.Identical); | 1307 return makeBinary(BuiltinOperator.Identical); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1376 } | 1378 } |
| 1377 } | 1379 } |
| 1378 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && | 1380 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && |
| 1379 lattice.isDefinitelyString(right, allowNull: trustPrimitives) && | 1381 lattice.isDefinitelyString(right, allowNull: trustPrimitives) && |
| 1380 opname == '+') { | 1382 opname == '+') { |
| 1381 // TODO(asgerf): Add IsString builtin so we can use a guard here. | 1383 // TODO(asgerf): Add IsString builtin so we can use a guard here. |
| 1382 return makeBinary(BuiltinOperator.StringConcatenate); | 1384 return makeBinary(BuiltinOperator.StringConcatenate); |
| 1383 } | 1385 } |
| 1384 } | 1386 } |
| 1385 } | 1387 } |
| 1386 if (node.selector.isOperator && node.dartArgumentsLength == 0) { | 1388 if (node.selector.isOperator && node.argumentRefs.length == 0) { |
| 1387 if (typeSystem.isDefinitelyNum(successType)) { | 1389 if (typeSystem.isDefinitelyNum(successType)) { |
| 1388 String opname = node.selector.name; | 1390 String opname = node.selector.name; |
| 1389 if (opname == '~') { | 1391 if (opname == '~') { |
| 1390 return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); | 1392 return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); |
| 1391 } | 1393 } |
| 1392 if (opname == 'unary-') { | 1394 if (opname == 'unary-') { |
| 1393 return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); | 1395 return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); |
| 1394 } | 1396 } |
| 1395 } | 1397 } |
| 1396 } | 1398 } |
| 1397 if (node.selector.isCall) { | 1399 if (node.selector.isCall) { |
| 1398 String name = node.selector.name; | 1400 String name = node.selector.name; |
| 1399 Primitive receiver = node.dartReceiver; | 1401 Primitive receiver = node.receiver; |
| 1400 AbstractConstantValue receiverValue = getValue(receiver); | 1402 AbstractConstantValue receiverValue = getValue(receiver); |
| 1401 if (name == 'remainder') { | 1403 if (name == 'remainder') { |
| 1402 if (node.dartArgumentsLength == 1) { | 1404 if (node.argumentRefs.length == 1) { |
| 1403 Primitive arg = node.dartArgument(0); | 1405 Primitive arg = node.argument(0); |
| 1404 AbstractConstantValue argValue = getValue(arg); | 1406 AbstractConstantValue argValue = getValue(arg); |
| 1405 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) && | 1407 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) && |
| 1406 lattice.isDefinitelyInt(argValue) && | 1408 lattice.isDefinitelyInt(argValue) && |
| 1407 isIntNotZero(argValue)) { | 1409 isIntNotZero(argValue)) { |
| 1408 return makeBinary(BuiltinOperator.NumRemainder, | 1410 return makeBinary(BuiltinOperator.NumRemainder, |
| 1409 guard: checkIsNumber); | 1411 guard: checkIsNumber); |
| 1410 } | 1412 } |
| 1411 } | 1413 } |
| 1412 } else if (name == 'codeUnitAt') { | 1414 } else if (name == 'codeUnitAt') { |
| 1413 if (node.dartArgumentsLength == 1) { | 1415 if (node.argumentRefs.length == 1) { |
| 1414 Primitive index = node.dartArgument(0); | 1416 Primitive index = node.argument(0); |
| 1415 if (lattice.isDefinitelyString(receiverValue) && | 1417 if (lattice.isDefinitelyString(receiverValue) && |
| 1416 lattice.isDefinitelyInt(getValue(index))) { | 1418 lattice.isDefinitelyInt(getValue(index))) { |
| 1417 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1419 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1418 receiver = makeBoundsCheck(cps, receiver, index); | 1420 receiver = makeBoundsCheck(cps, receiver, index); |
| 1419 ApplyBuiltinOperator get = | 1421 ApplyBuiltinOperator get = |
| 1420 cps.applyBuiltin(BuiltinOperator.CharCodeAt, | 1422 cps.applyBuiltin(BuiltinOperator.CharCodeAt, |
| 1421 <Primitive>[receiver, index]); | 1423 <Primitive>[receiver, index]); |
| 1422 node.replaceUsesWith(get); | 1424 node.replaceUsesWith(get); |
| 1423 get.hint = node.hint; | 1425 get.hint = node.hint; |
| 1424 return cps; | 1426 return cps; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1438 bool isInterceptedSelector(Selector selector) { | 1440 bool isInterceptedSelector(Selector selector) { |
| 1439 return backend.isInterceptedSelector(selector); | 1441 return backend.isInterceptedSelector(selector); |
| 1440 } | 1442 } |
| 1441 | 1443 |
| 1442 /// If [node] is a getter or setter invocation, tries to replace the | 1444 /// If [node] is a getter or setter invocation, tries to replace the |
| 1443 /// invocation with a direct access to a field. | 1445 /// invocation with a direct access to a field. |
| 1444 /// | 1446 /// |
| 1445 /// Returns `true` if the node was replaced. | 1447 /// Returns `true` if the node was replaced. |
| 1446 Primitive specializeFieldAccess(InvokeMethod node) { | 1448 Primitive specializeFieldAccess(InvokeMethod node) { |
| 1447 if (!node.selector.isGetter && !node.selector.isSetter) return null; | 1449 if (!node.selector.isGetter && !node.selector.isSetter) return null; |
| 1448 AbstractConstantValue receiver = getValue(node.dartReceiver); | 1450 AbstractConstantValue receiver = getValue(node.receiver); |
| 1449 Element target = | 1451 Element target = |
| 1450 typeSystem.locateSingleElement(receiver.type, node.selector); | 1452 typeSystem.locateSingleElement(receiver.type, node.selector); |
| 1451 if (target is! FieldElement) return null; | 1453 if (target is! FieldElement) return null; |
| 1452 // TODO(asgerf): Inlining native fields will make some tests pass for the | 1454 // TODO(asgerf): Inlining native fields will make some tests pass for the |
| 1453 // wrong reason, so for testing reasons avoid inlining them. | 1455 // wrong reason, so for testing reasons avoid inlining them. |
| 1454 if (backend.isNative(target) || backend.isJsInterop(target)) { | 1456 if (backend.isNative(target) || backend.isJsInterop(target)) { |
| 1455 return null; | 1457 return null; |
| 1456 } | 1458 } |
| 1457 if (node.selector.isGetter) { | 1459 if (node.selector.isGetter) { |
| 1458 return new GetField(node.dartReceiver, target); | 1460 return new GetField(node.receiver, target); |
| 1459 } else { | 1461 } else { |
| 1460 if (target.isFinal) return null; | 1462 if (target.isFinal) return null; |
| 1461 assert(node.hasNoUses); | 1463 assert(node.hasNoUses); |
| 1462 return new SetField(node.dartReceiver, | 1464 return new SetField(node.receiver, |
| 1463 target, | 1465 target, |
| 1464 node.dartArgument(0)); | 1466 node.argument(0)); |
| 1465 } | 1467 } |
| 1466 } | 1468 } |
| 1467 | 1469 |
| 1468 /// Create a check that throws if [index] is not a valid index on [list]. | 1470 /// Create a check that throws if [index] is not a valid index on [list]. |
| 1469 /// | 1471 /// |
| 1470 /// This function assumes that [index] is an integer. | 1472 /// This function assumes that [index] is an integer. |
| 1471 /// | 1473 /// |
| 1472 /// Returns a CPS fragment whose context is the branch where no error | 1474 /// Returns a CPS fragment whose context is the branch where no error |
| 1473 /// was thrown. | 1475 /// was thrown. |
| 1474 Primitive makeBoundsCheck(CpsFragment cps, | 1476 Primitive makeBoundsCheck(CpsFragment cps, |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1508 cps.ifTruthy(lengthChanged).invokeStaticThrower( | 1510 cps.ifTruthy(lengthChanged).invokeStaticThrower( |
| 1509 helpers.throwConcurrentModificationError, | 1511 helpers.throwConcurrentModificationError, |
| 1510 <Primitive>[list]); | 1512 <Primitive>[list]); |
| 1511 return cps; | 1513 return cps; |
| 1512 } | 1514 } |
| 1513 | 1515 |
| 1514 /// Tries to replace [node] with a direct `length` or index access. | 1516 /// Tries to replace [node] with a direct `length` or index access. |
| 1515 /// | 1517 /// |
| 1516 /// Returns `true` if the node was replaced. | 1518 /// Returns `true` if the node was replaced. |
| 1517 specializeIndexableAccess(InvokeMethod node) { | 1519 specializeIndexableAccess(InvokeMethod node) { |
| 1518 Primitive receiver = node.dartReceiver; | 1520 Primitive receiver = node.receiver; |
| 1519 AbstractConstantValue receiverValue = getValue(receiver); | 1521 AbstractConstantValue receiverValue = getValue(receiver); |
| 1520 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, | 1522 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, |
| 1521 allowNull: true)) { | 1523 allowNull: true)) { |
| 1522 return null; | 1524 return null; |
| 1523 } | 1525 } |
| 1524 switch (node.selector.name) { | 1526 switch (node.selector.name) { |
| 1525 case 'length': | 1527 case 'length': |
| 1526 if (node.selector.isGetter) { | 1528 if (node.selector.isGetter) { |
| 1527 return new GetLength(receiver); | 1529 return new GetLength(receiver); |
| 1528 } | 1530 } |
| 1529 if (node.selector.isSetter) { | 1531 if (node.selector.isSetter) { |
| 1530 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, | 1532 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, |
| 1531 allowNull: true)) { | 1533 allowNull: true)) { |
| 1532 return null; | 1534 return null; |
| 1533 } | 1535 } |
| 1534 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1536 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1535 Primitive newLength = node.dartArgument(0); | 1537 Primitive newLength = node.argument(0); |
| 1536 if (!typeSystem.isDefinitelyUint(newLength.type)) { | 1538 if (!typeSystem.isDefinitelyUint(newLength.type)) { |
| 1537 // TODO(asgerf): We could let the SetLength instruction throw for | 1539 // TODO(asgerf): We could let the SetLength instruction throw for |
| 1538 // negative right-hand sides (see length setter in js_array.dart). | 1540 // negative right-hand sides (see length setter in js_array.dart). |
| 1539 if (compiler.trustPrimitives) { | 1541 if (compiler.trustPrimitives) { |
| 1540 newLength = cps.refine(newLength, typeSystem.uint32Type); | 1542 newLength = cps.refine(newLength, typeSystem.uint32Type); |
| 1541 newLength.type = typeSystem.uint32Type; | 1543 newLength.type = typeSystem.uint32Type; |
| 1542 } else { | 1544 } else { |
| 1543 return null; | 1545 return null; |
| 1544 } | 1546 } |
| 1545 } | 1547 } |
| 1546 cps.letPrim(new ApplyBuiltinMethod( | 1548 cps.letPrim(new ApplyBuiltinMethod( |
| 1547 BuiltinMethod.SetLength, | 1549 BuiltinMethod.SetLength, |
| 1548 receiver, | 1550 receiver, |
| 1549 [newLength], | 1551 [newLength], |
| 1550 node.sourceInformation)); | 1552 node.sourceInformation)); |
| 1551 if (!typeSystem.isDefinitelyUint32(newLength.type)) { | 1553 if (!typeSystem.isDefinitelyUint32(newLength.type)) { |
| 1552 // If the setter succeeded, the length must have been a uint32. | 1554 // If the setter succeeded, the length must have been a uint32. |
| 1553 cps.refine(newLength, typeSystem.uint32Type); | 1555 cps.refine(newLength, typeSystem.uint32Type); |
| 1554 } | 1556 } |
| 1555 return cps; | 1557 return cps; |
| 1556 } | 1558 } |
| 1557 return null; | 1559 return null; |
| 1558 | 1560 |
| 1559 case '[]': | 1561 case '[]': |
| 1560 Primitive index = node.dartArgument(0); | 1562 Primitive index = node.argument(0); |
| 1561 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1563 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1562 receiver = makeBoundsCheck(cps, receiver, index); | 1564 receiver = makeBoundsCheck(cps, receiver, index); |
| 1563 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); | 1565 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); |
| 1564 node.replaceUsesWith(get); | 1566 node.replaceUsesWith(get); |
| 1565 // TODO(asgerf): Make replaceUsesWith set the hint? | 1567 // TODO(asgerf): Make replaceUsesWith set the hint? |
| 1566 get.hint = node.hint; | 1568 get.hint = node.hint; |
| 1567 return cps; | 1569 return cps; |
| 1568 | 1570 |
| 1569 case '[]=': | 1571 case '[]=': |
| 1570 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, | 1572 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, |
| 1571 allowNull: true)) { | 1573 allowNull: true)) { |
| 1572 return null; | 1574 return null; |
| 1573 } | 1575 } |
| 1574 Primitive index = node.dartArgument(0); | 1576 Primitive index = node.argument(0); |
| 1575 Primitive value = node.dartArgument(1); | 1577 Primitive value = node.argument(1); |
| 1576 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1578 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1577 receiver = makeBoundsCheck(cps, receiver, index); | 1579 receiver = makeBoundsCheck(cps, receiver, index); |
| 1578 cps.letPrim(new SetIndex(receiver, index, value)); | 1580 cps.letPrim(new SetIndex(receiver, index, value)); |
| 1579 assert(node.hasNoUses); | 1581 assert(node.hasNoUses); |
| 1580 return cps; | 1582 return cps; |
| 1581 | 1583 |
| 1582 case 'isEmpty': | 1584 case 'isEmpty': |
| 1583 if (!node.selector.isGetter) return null; | 1585 if (!node.selector.isGetter) return null; |
| 1584 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1586 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1585 Primitive length = cps.letPrim(new GetLength(receiver)); | 1587 Primitive length = cps.letPrim(new GetLength(receiver)); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1603 | 1605 |
| 1604 default: | 1606 default: |
| 1605 return null; | 1607 return null; |
| 1606 } | 1608 } |
| 1607 } | 1609 } |
| 1608 | 1610 |
| 1609 /// Tries to replace [node] with one or more direct array access operations. | 1611 /// Tries to replace [node] with one or more direct array access operations. |
| 1610 /// | 1612 /// |
| 1611 /// Returns `true` if the node was replaced. | 1613 /// Returns `true` if the node was replaced. |
| 1612 CpsFragment specializeArrayAccess(InvokeMethod node) { | 1614 CpsFragment specializeArrayAccess(InvokeMethod node) { |
| 1613 Primitive list = node.dartReceiver; | 1615 Primitive list = node.receiver; |
| 1614 AbstractConstantValue listValue = getValue(list); | 1616 AbstractConstantValue listValue = getValue(list); |
| 1615 // Ensure that the object is a native list or null. | 1617 // Ensure that the object is a native list or null. |
| 1616 if (!lattice.isDefinitelyArray(listValue, allowNull: true)) { | 1618 if (!lattice.isDefinitelyArray(listValue, allowNull: true)) { |
| 1617 return null; | 1619 return null; |
| 1618 } | 1620 } |
| 1619 bool isFixedLength = | 1621 bool isFixedLength = |
| 1620 lattice.isDefinitelyFixedArray(listValue, allowNull: true); | 1622 lattice.isDefinitelyFixedArray(listValue, allowNull: true); |
| 1621 bool isExtendable = | 1623 bool isExtendable = |
| 1622 lattice.isDefinitelyExtendableArray(listValue, allowNull: true); | 1624 lattice.isDefinitelyExtendableArray(listValue, allowNull: true); |
| 1623 SourceInformation sourceInfo = node.sourceInformation; | 1625 SourceInformation sourceInfo = node.sourceInformation; |
| 1624 switch (node.selector.name) { | 1626 switch (node.selector.name) { |
| 1625 case 'add': | 1627 case 'add': |
| 1626 if (!node.selector.isCall || | 1628 if (!node.selector.isCall || |
| 1627 node.selector.positionalArgumentCount != 1 || | 1629 node.selector.positionalArgumentCount != 1 || |
| 1628 node.selector.namedArgumentCount != 0) { | 1630 node.selector.namedArgumentCount != 0) { |
| 1629 return null; | 1631 return null; |
| 1630 } | 1632 } |
| 1631 if (!isExtendable) return null; | 1633 if (!isExtendable) return null; |
| 1632 Primitive addedItem = node.dartArgument(0); | 1634 Primitive addedItem = node.argument(0); |
| 1633 CpsFragment cps = new CpsFragment(sourceInfo); | 1635 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1634 cps.invokeBuiltin(BuiltinMethod.Push, | 1636 cps.invokeBuiltin(BuiltinMethod.Push, |
| 1635 list, | 1637 list, |
| 1636 <Primitive>[addedItem]); | 1638 <Primitive>[addedItem]); |
| 1637 if (node.hasAtLeastOneUse) { | 1639 if (node.hasAtLeastOneUse) { |
| 1638 node.replaceUsesWith(cps.makeNull()); | 1640 node.replaceUsesWith(cps.makeNull()); |
| 1639 } | 1641 } |
| 1640 return cps; | 1642 return cps; |
| 1641 | 1643 |
| 1642 case 'removeLast': | 1644 case 'removeLast': |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1654 removedItem.hint = node.hint; | 1656 removedItem.hint = node.hint; |
| 1655 node.replaceUsesWith(removedItem); | 1657 node.replaceUsesWith(removedItem); |
| 1656 return cps; | 1658 return cps; |
| 1657 | 1659 |
| 1658 case 'addAll': | 1660 case 'addAll': |
| 1659 if (!node.selector.isCall || | 1661 if (!node.selector.isCall || |
| 1660 node.selector.argumentCount != 1) { | 1662 node.selector.argumentCount != 1) { |
| 1661 return null; | 1663 return null; |
| 1662 } | 1664 } |
| 1663 if (!isExtendable) return null; | 1665 if (!isExtendable) return null; |
| 1664 Primitive addedList = node.dartArgument(0); | 1666 Primitive addedList = node.argument(0); |
| 1665 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). | 1667 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). |
| 1666 // Ensure that the list is not mutated between creation and use. | 1668 // Ensure that the list is not mutated between creation and use. |
| 1667 // We aim for the common case where this is the only use of the list, | 1669 // We aim for the common case where this is the only use of the list, |
| 1668 // which also guarantees that this list is not mutated before use. | 1670 // which also guarantees that this list is not mutated before use. |
| 1669 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { | 1671 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
| 1670 return null; | 1672 return null; |
| 1671 } | 1673 } |
| 1672 LiteralList addedLiteral = addedList; | 1674 LiteralList addedLiteral = addedList; |
| 1673 CpsFragment cps = new CpsFragment(sourceInfo); | 1675 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1674 for (Reference value in addedLiteral.valueRefs) { | 1676 for (Reference value in addedLiteral.valueRefs) { |
| 1675 cps.invokeBuiltin(BuiltinMethod.Push, | 1677 cps.invokeBuiltin(BuiltinMethod.Push, |
| 1676 list, | 1678 list, |
| 1677 <Primitive>[value.definition]); | 1679 <Primitive>[value.definition]); |
| 1678 } | 1680 } |
| 1679 if (node.hasAtLeastOneUse) { | 1681 if (node.hasAtLeastOneUse) { |
| 1680 node.replaceUsesWith(cps.makeNull()); | 1682 node.replaceUsesWith(cps.makeNull()); |
| 1681 } | 1683 } |
| 1682 return cps; | 1684 return cps; |
| 1683 | 1685 |
| 1684 case 'elementAt': | 1686 case 'elementAt': |
| 1685 if (!node.selector.isCall || | 1687 if (!node.selector.isCall || |
| 1686 node.selector.positionalArgumentCount != 1 || | 1688 node.selector.positionalArgumentCount != 1 || |
| 1687 node.selector.namedArgumentCount != 0) { | 1689 node.selector.namedArgumentCount != 0) { |
| 1688 return null; | 1690 return null; |
| 1689 } | 1691 } |
| 1690 if (listValue.isNullable) return null; | 1692 if (listValue.isNullable) return null; |
| 1691 Primitive index = node.dartArgument(0); | 1693 Primitive index = node.argument(0); |
| 1692 if (!lattice.isDefinitelyInt(getValue(index))) return null; | 1694 if (!lattice.isDefinitelyInt(getValue(index))) return null; |
| 1693 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1695 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1694 list = makeBoundsCheck(cps, list, index); | 1696 list = makeBoundsCheck(cps, list, index); |
| 1695 GetIndex get = cps.letPrim(new GetIndex(list, index)); | 1697 GetIndex get = cps.letPrim(new GetIndex(list, index)); |
| 1696 get.hint = node.hint; | 1698 get.hint = node.hint; |
| 1697 node.replaceUsesWith(get); | 1699 node.replaceUsesWith(get); |
| 1698 return cps; | 1700 return cps; |
| 1699 | 1701 |
| 1700 case 'forEach': | 1702 case 'forEach': |
| 1701 Element element = | 1703 Element element = |
| 1702 compiler.world.locateSingleElement(node.selector, listValue.type); | 1704 compiler.world.locateSingleElement(node.selector, listValue.type); |
| 1703 if (element == null || | 1705 if (element == null || |
| 1704 !element.isFunction || | 1706 !element.isFunction || |
| 1705 !node.selector.isCall) return null; | 1707 !node.selector.isCall) return null; |
| 1706 assert(node.selector.positionalArgumentCount == 1); | 1708 assert(node.selector.positionalArgumentCount == 1); |
| 1707 assert(node.selector.namedArgumentCount == 0); | 1709 assert(node.selector.namedArgumentCount == 0); |
| 1708 FunctionDefinition target = functionCompiler.compileToCpsIr(element); | 1710 FunctionDefinition target = functionCompiler.compileToCpsIr(element); |
| 1709 | 1711 |
| 1710 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1712 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1711 Primitive result = cps.inlineFunction(target, | 1713 Primitive result = cps.inlineFunction(target, |
| 1712 node.receiver, | 1714 node.receiver, |
| 1713 node.arguments.toList(), | 1715 node.arguments.toList(), |
| 1716 interceptor: node.interceptor, |
| 1714 hint: node.hint); | 1717 hint: node.hint); |
| 1715 node.replaceUsesWith(result); | 1718 node.replaceUsesWith(result); |
| 1716 return cps; | 1719 return cps; |
| 1717 | 1720 |
| 1718 case 'iterator': | 1721 case 'iterator': |
| 1719 // TODO(asgerf): This should be done differently. | 1722 // TODO(asgerf): This should be done differently. |
| 1720 // The types are recomputed in a very error-prone manner. | 1723 // The types are recomputed in a very error-prone manner. |
| 1721 if (!node.selector.isGetter) return null; | 1724 if (!node.selector.isGetter) return null; |
| 1722 Primitive iterator = node; | 1725 Primitive iterator = node; |
| 1723 LetPrim iteratorBinding = node.parent; | 1726 LetPrim iteratorBinding = node.parent; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1891 /// => | 1894 /// => |
| 1892 /// obj.foo$<n>(<args>) | 1895 /// obj.foo$<n>(<args>) |
| 1893 /// | 1896 /// |
| 1894 Primitive specializeClosureCall(InvokeMethod node) { | 1897 Primitive specializeClosureCall(InvokeMethod node) { |
| 1895 Selector call = node.selector; | 1898 Selector call = node.selector; |
| 1896 if (!call.isClosureCall) return null; | 1899 if (!call.isClosureCall) return null; |
| 1897 | 1900 |
| 1898 assert(!isInterceptedSelector(call)); | 1901 assert(!isInterceptedSelector(call)); |
| 1899 assert(call.argumentCount == node.argumentRefs.length); | 1902 assert(call.argumentCount == node.argumentRefs.length); |
| 1900 | 1903 |
| 1901 Primitive tearOff = node.dartReceiver.effectiveDefinition; | 1904 Primitive tearOff = node.receiver.effectiveDefinition; |
| 1902 // Note: We don't know if [tearOff] is actually a tear-off. | 1905 // Note: We don't know if [tearOff] is actually a tear-off. |
| 1903 // We name variables based on the pattern we are trying to match. | 1906 // We name variables based on the pattern we are trying to match. |
| 1904 | 1907 |
| 1905 if (tearOff is GetStatic && tearOff.element.isFunction) { | 1908 if (tearOff is GetStatic && tearOff.element.isFunction) { |
| 1906 FunctionElement target = tearOff.element; | 1909 FunctionElement target = tearOff.element; |
| 1907 FunctionSignature signature = target.functionSignature; | 1910 FunctionSignature signature = target.functionSignature; |
| 1908 | 1911 |
| 1909 // If the selector does not apply, don't bother (will throw at runtime). | 1912 // If the selector does not apply, don't bother (will throw at runtime). |
| 1910 if (!call.signatureApplies(target)) return null; | 1913 if (!call.signatureApplies(target)) return null; |
| 1911 | 1914 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2021 // engines typically do poor optimization of the entire function containing | 2024 // engines typically do poor optimization of the entire function containing |
| 2022 // the 'try'. | 2025 // the 'try'. |
| 2023 if (functionElement.resolvedAst.elements.containsTryStatement) return null; | 2026 if (functionElement.resolvedAst.elements.containsTryStatement) return null; |
| 2024 | 2027 |
| 2025 FunctionDefinition target = | 2028 FunctionDefinition target = |
| 2026 functionCompiler.compileToCpsIr(functionElement); | 2029 functionCompiler.compileToCpsIr(functionElement); |
| 2027 | 2030 |
| 2028 // Accesses to closed-over values are field access primitives. We we don't | 2031 // Accesses to closed-over values are field access primitives. We we don't |
| 2029 // inline if there are other uses of 'this' since that could be an escape or | 2032 // inline if there are other uses of 'this' since that could be an escape or |
| 2030 // a recursive call. | 2033 // a recursive call. |
| 2031 for (Reference ref = target.thisParameter.firstRef; | 2034 for (Reference ref = target.receiverParameter.firstRef; |
| 2032 ref != null; | 2035 ref != null; |
| 2033 ref = ref.next) { | 2036 ref = ref.next) { |
| 2034 Node use = ref.parent; | 2037 Node use = ref.parent; |
| 2035 if (use is GetField) continue; | 2038 if (use is GetField) continue; |
| 2036 // Closures do not currently have writable fields, but closure conversion | 2039 // Closures do not currently have writable fields, but closure conversion |
| 2037 // could esily be changed to allocate some cells in a closure object. | 2040 // could esily be changed to allocate some cells in a closure object. |
| 2038 if (use is SetField && ref == use.objectRef) continue; | 2041 if (use is SetField && ref == use.objectRef) continue; |
| 2039 return null; | 2042 return null; |
| 2040 } | 2043 } |
| 2041 | 2044 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2067 } | 2070 } |
| 2068 | 2071 |
| 2069 visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2072 visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 2070 Element target = node.target; | 2073 Element target = node.target; |
| 2071 if (target is ConstructorBodyElement) { | 2074 if (target is ConstructorBodyElement) { |
| 2072 ConstructorBodyElement constructorBody = target; | 2075 ConstructorBodyElement constructorBody = target; |
| 2073 target = constructorBody.constructor; | 2076 target = constructorBody.constructor; |
| 2074 } | 2077 } |
| 2075 node.effects = | 2078 node.effects = |
| 2076 Effects.from(compiler.world.getSideEffectsOfElement(target)); | 2079 Effects.from(compiler.world.getSideEffectsOfElement(target)); |
| 2077 TypeMask receiverType = node.dartReceiver.type; | 2080 TypeMask receiverType = node.receiver.type; |
| 2078 if (node.callingConvention == CallingConvention.Intercepted && | 2081 if (node.callingConvention == CallingConvention.Intercepted && |
| 2079 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2082 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
| 2080 // Some direct calls take an interceptor because the target class is | 2083 // Some direct calls take an interceptor because the target class is |
| 2081 // mixed into a native class. If it is known at the call site that the | 2084 // mixed into a native class. If it is known at the call site that the |
| 2082 // receiver is non-intercepted, get rid of the interceptor. | 2085 // receiver is non-intercepted, get rid of the interceptor. |
| 2083 node.receiverRef.changeTo(node.dartReceiver); | 2086 node.interceptorRef.changeTo(node.receiver); |
| 2084 } | 2087 } |
| 2085 } | 2088 } |
| 2086 | 2089 |
| 2087 visitInvokeMethod(InvokeMethod node) { | 2090 visitInvokeMethod(InvokeMethod node) { |
| 2088 var specialized = | 2091 var specialized = |
| 2089 specializeOperatorCall(node) ?? | 2092 specializeOperatorCall(node) ?? |
| 2090 specializeFieldAccess(node) ?? | 2093 specializeFieldAccess(node) ?? |
| 2091 specializeIndexableAccess(node) ?? | 2094 specializeIndexableAccess(node) ?? |
| 2092 specializeArrayAccess(node) ?? | 2095 specializeArrayAccess(node) ?? |
| 2093 specializeSingleUseClosureCall(node) ?? | 2096 specializeSingleUseClosureCall(node) ?? |
| 2094 specializeClosureCall(node); | 2097 specializeClosureCall(node); |
| 2095 if (specialized != null) return specialized; | 2098 if (specialized != null) return specialized; |
| 2096 | 2099 |
| 2097 TypeMask receiverType = node.dartReceiver.type; | 2100 TypeMask receiverType = node.receiver.type; |
| 2098 node.mask = typeSystem.intersection(node.mask, receiverType); | 2101 node.mask = typeSystem.intersection(node.mask, receiverType); |
| 2099 | 2102 |
| 2100 node.effects = Effects.from( | 2103 node.effects = Effects.from( |
| 2101 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); | 2104 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); |
| 2102 | 2105 |
| 2103 bool canBeNonThrowingCallOnNull = | 2106 bool canBeNonThrowingCallOnNull = |
| 2104 selectorsOnNull.contains(node.selector) && | 2107 selectorsOnNull.contains(node.selector) && |
| 2105 receiverType.isNullable; | 2108 receiverType.isNullable; |
| 2106 | 2109 |
| 2107 if (node.callingConvention == CallingConvention.Intercepted && | 2110 if (node.callingConvention == CallingConvention.Intercepted && |
| 2108 !canBeNonThrowingCallOnNull && | 2111 !canBeNonThrowingCallOnNull && |
| 2109 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2112 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
| 2110 // Use the Dart receiver as the JS receiver. This changes the wording of | 2113 // Use the Dart receiver as the JS receiver. This changes the wording of |
| 2111 // the error message when the receiver is null, but we accept this. | 2114 // the error message when the receiver is null, but we accept this. |
| 2112 node.receiverRef.changeTo(node.dartReceiver); | 2115 node.interceptorRef.changeTo(node.receiver); |
| 2113 | 2116 |
| 2114 // Replace the extra receiver argument with a dummy value if the | 2117 // Replace the extra receiver argument with a dummy value if the |
| 2115 // target definitely does not use it. | 2118 // target definitely does not use it. |
| 2116 if (typeSystem.targetIgnoresReceiverArgument(receiverType, | 2119 if (typeSystem.targetIgnoresReceiverArgument(receiverType, |
| 2117 node.selector)) { | 2120 node.selector)) { |
| 2118 Constant dummy = makeConstantPrimitive(new IntConstantValue(0)); | 2121 node.makeDummyIntercepted(); |
| 2119 new LetPrim(dummy).insertAbove(node.parent); | |
| 2120 node.argumentRefs[0].changeTo(dummy); | |
| 2121 node.callingConvention = CallingConvention.DummyIntercepted; | |
| 2122 } | 2122 } |
| 2123 } | 2123 } |
| 2124 } | 2124 } |
| 2125 | 2125 |
| 2126 CpsFragment visitTypeCast(TypeCast node) { | 2126 CpsFragment visitTypeCast(TypeCast node) { |
| 2127 AbstractConstantValue value = getValue(node.value); | 2127 AbstractConstantValue value = getValue(node.value); |
| 2128 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { | 2128 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { |
| 2129 case AbstractBool.Maybe: | 2129 case AbstractBool.Maybe: |
| 2130 case AbstractBool.Nothing: | 2130 case AbstractBool.Nothing: |
| 2131 return null; | 2131 return null; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2148 Primitive argument = node.argument(0); | 2148 Primitive argument = node.argument(0); |
| 2149 AbstractConstantValue value = getValue(argument); | 2149 AbstractConstantValue value = getValue(argument); |
| 2150 if (lattice.isDefinitelyString(value)) { | 2150 if (lattice.isDefinitelyString(value)) { |
| 2151 node.replaceUsesWith(argument); | 2151 node.replaceUsesWith(argument); |
| 2152 return new CpsFragment(); | 2152 return new CpsFragment(); |
| 2153 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { | 2153 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { |
| 2154 TypeMask toStringReturn = typeSystem.getInvokeReturnType( | 2154 TypeMask toStringReturn = typeSystem.getInvokeReturnType( |
| 2155 Selectors.toString_, value.type); | 2155 Selectors.toString_, value.type); |
| 2156 if (typeSystem.isDefinitelyString(toStringReturn)) { | 2156 if (typeSystem.isDefinitelyString(toStringReturn)) { |
| 2157 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2157 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 2158 Primitive invoke = cps.invokeMethod(argument, | 2158 Primitive invoke = cps.invokeMethod( |
| 2159 argument, |
| 2159 Selectors.toString_, | 2160 Selectors.toString_, |
| 2160 value.type, | 2161 value.type, |
| 2161 [cps.makeZero()], | 2162 [], |
| 2162 CallingConvention.DummyIntercepted); | 2163 callingConvention: CallingConvention.DummyIntercepted); |
| 2163 node.replaceUsesWith(invoke); | 2164 node.replaceUsesWith(invoke); |
| 2164 return cps; | 2165 return cps; |
| 2165 } | 2166 } |
| 2166 } | 2167 } |
| 2167 } else if (node.target == compiler.identicalFunction) { | 2168 } else if (node.target == compiler.identicalFunction) { |
| 2168 if (node.argumentRefs.length == 2) { | 2169 if (node.argumentRefs.length == 2) { |
| 2169 return new ApplyBuiltinOperator(BuiltinOperator.Identical, | 2170 return new ApplyBuiltinOperator(BuiltinOperator.Identical, |
| 2170 [node.argument(0), node.argument(1)], | 2171 [node.argument(0), node.argument(1)], |
| 2171 node.sourceInformation); | 2172 node.sourceInformation); |
| 2172 } | 2173 } |
| (...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2673 } | 2674 } |
| 2674 | 2675 |
| 2675 bool isInterceptedSelector(Selector selector) { | 2676 bool isInterceptedSelector(Selector selector) { |
| 2676 return backend.isInterceptedSelector(selector); | 2677 return backend.isInterceptedSelector(selector); |
| 2677 } | 2678 } |
| 2678 | 2679 |
| 2679 // -------------------------- Visitor overrides ------------------------------ | 2680 // -------------------------- Visitor overrides ------------------------------ |
| 2680 void visit(Node node) { node.accept(this); } | 2681 void visit(Node node) { node.accept(this); } |
| 2681 | 2682 |
| 2682 void visitFunctionDefinition(FunctionDefinition node) { | 2683 void visitFunctionDefinition(FunctionDefinition node) { |
| 2683 bool isIntercepted = backend.isInterceptedMethod(node.element); | 2684 if (node.interceptorParameter != null) { |
| 2684 | 2685 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); |
| 2686 } |
| 2685 // If the abstract value of the function parameters is Nothing, use the | 2687 // If the abstract value of the function parameters is Nothing, use the |
| 2686 // inferred parameter type. Otherwise (e.g., when inlining) do not | 2688 // inferred parameter type. Otherwise (e.g., when inlining) do not |
| 2687 // change the abstract value. | 2689 // change the abstract value. |
| 2688 if (node.thisParameter != null && getValue(node.thisParameter).isNothing) { | 2690 if (node.receiverParameter != null && |
| 2689 if (isIntercepted && | 2691 getValue(node.receiverParameter).isNothing) { |
| 2690 !typeSystem.methodIgnoresReceiverArgument(node.element)) { | 2692 setValue(node.receiverParameter, |
| 2691 setValue(node.thisParameter, nonConstant(typeSystem.nonNullType)); | 2693 nonConstant(typeSystem.getReceiverType(node.element))); |
| 2692 } else { | |
| 2693 setValue(node.thisParameter, | |
| 2694 nonConstant(typeSystem.getReceiverType(node.element))); | |
| 2695 } | |
| 2696 } | |
| 2697 if (isIntercepted && getValue(node.parameters[0]).isNothing) { | |
| 2698 if (typeSystem.methodIgnoresReceiverArgument(node.element)) { | |
| 2699 setValue(node.parameters[0], nonConstant()); | |
| 2700 } else { | |
| 2701 setValue(node.parameters[0], | |
| 2702 nonConstant(typeSystem.getReceiverType(node.element))); | |
| 2703 } | |
| 2704 } | 2694 } |
| 2705 bool hasParameterWithoutValue = false; | 2695 bool hasParameterWithoutValue = false; |
| 2706 for (Parameter param in node.parameters.skip(isIntercepted ? 1 : 0)) { | 2696 for (Parameter param in node.parameters) { |
| 2707 if (getValue(param).isNothing) { | 2697 if (getValue(param).isNothing) { |
| 2708 TypeMask type = param.hint is ParameterElement | 2698 TypeMask type = param.hint is ParameterElement |
| 2709 ? typeSystem.getParameterType(param.hint) | 2699 ? typeSystem.getParameterType(param.hint) |
| 2710 : typeSystem.dynamicType; | 2700 : typeSystem.dynamicType; |
| 2711 setValue(param, lattice.fromMask(type)); | 2701 setValue(param, lattice.fromMask(type)); |
| 2712 if (type.isEmpty) hasParameterWithoutValue = true; | 2702 if (type.isEmpty) hasParameterWithoutValue = true; |
| 2713 } | 2703 } |
| 2714 } | 2704 } |
| 2715 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. | 2705 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. |
| 2716 push(node.body); | 2706 push(node.body); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2766 // Forward the constant status of all continuation invokes to the | 2756 // Forward the constant status of all continuation invokes to the |
| 2767 // continuation. Note that this is effectively a phi node in SSA terms. | 2757 // continuation. Note that this is effectively a phi node in SSA terms. |
| 2768 for (int i = 0; i < node.argumentRefs.length; i++) { | 2758 for (int i = 0; i < node.argumentRefs.length; i++) { |
| 2769 Primitive def = node.argument(i); | 2759 Primitive def = node.argument(i); |
| 2770 AbstractConstantValue cell = getValue(def); | 2760 AbstractConstantValue cell = getValue(def); |
| 2771 setValue(cont.parameters[i], cell); | 2761 setValue(cont.parameters[i], cell); |
| 2772 } | 2762 } |
| 2773 } | 2763 } |
| 2774 | 2764 |
| 2775 void visitInvokeMethod(InvokeMethod node) { | 2765 void visitInvokeMethod(InvokeMethod node) { |
| 2776 AbstractConstantValue receiver = getValue(node.dartReceiver); | 2766 AbstractConstantValue receiver = getValue(node.receiver); |
| 2777 if (receiver.isNothing) { | 2767 if (receiver.isNothing) { |
| 2778 return setResult(node, lattice.nothing); | 2768 return setResult(node, lattice.nothing); |
| 2779 } | 2769 } |
| 2780 | 2770 |
| 2781 void finish(AbstractConstantValue result, {bool canReplace: false}) { | 2771 void finish(AbstractConstantValue result, {bool canReplace: false}) { |
| 2782 if (result == null) { | 2772 if (result == null) { |
| 2783 canReplace = false; | 2773 canReplace = false; |
| 2784 result = lattice.getInvokeReturnType(node.selector, receiver); | 2774 result = lattice.getInvokeReturnType(node.selector, receiver); |
| 2785 } | 2775 } |
| 2786 setResult(node, result, canReplace: canReplace); | 2776 setResult(node, result, canReplace: canReplace); |
| 2787 } | 2777 } |
| 2788 | 2778 |
| 2789 if (node.selector.isGetter) { | 2779 if (node.selector.isGetter) { |
| 2790 // Constant fold known length of containers. | 2780 // Constant fold known length of containers. |
| 2791 if (node.selector == Selectors.length) { | 2781 if (node.selector == Selectors.length) { |
| 2792 if (typeSystem.isDefinitelyIndexable(receiver.type, allowNull: true)) { | 2782 if (typeSystem.isDefinitelyIndexable(receiver.type, allowNull: true)) { |
| 2793 AbstractConstantValue length = lattice.lengthSpecial(receiver); | 2783 AbstractConstantValue length = lattice.lengthSpecial(receiver); |
| 2794 return finish(length, canReplace: !receiver.isNullable); | 2784 return finish(length, canReplace: !receiver.isNullable); |
| 2795 } | 2785 } |
| 2796 } | 2786 } |
| 2797 return finish(null); | 2787 return finish(null); |
| 2798 } | 2788 } |
| 2799 | 2789 |
| 2800 if (node.selector.isCall) { | 2790 if (node.selector.isCall) { |
| 2801 if (node.selector == Selectors.codeUnitAt) { | 2791 if (node.selector == Selectors.codeUnitAt) { |
| 2802 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2792 AbstractConstantValue right = getValue(node.argument(0)); |
| 2803 AbstractConstantValue result = | 2793 AbstractConstantValue result = |
| 2804 lattice.codeUnitAtSpecial(receiver, right); | 2794 lattice.codeUnitAtSpecial(receiver, right); |
| 2805 return finish(result, canReplace: !receiver.isNullable); | 2795 return finish(result, canReplace: !receiver.isNullable); |
| 2806 } | 2796 } |
| 2807 return finish(null); | 2797 return finish(null); |
| 2808 } | 2798 } |
| 2809 | 2799 |
| 2810 if (node.selector == Selectors.index) { | 2800 if (node.selector == Selectors.index) { |
| 2811 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2801 AbstractConstantValue right = getValue(node.argument(0)); |
| 2812 AbstractConstantValue result = lattice.indexSpecial(receiver, right); | 2802 AbstractConstantValue result = lattice.indexSpecial(receiver, right); |
| 2813 return finish(result, canReplace: !receiver.isNullable); | 2803 return finish(result, canReplace: !receiver.isNullable); |
| 2814 } | 2804 } |
| 2815 | 2805 |
| 2816 if (!node.selector.isOperator) { | 2806 if (!node.selector.isOperator) { |
| 2817 return finish(null); | 2807 return finish(null); |
| 2818 } | 2808 } |
| 2819 | 2809 |
| 2820 // Calculate the resulting constant if possible. | 2810 // Calculate the resulting constant if possible. |
| 2821 String opname = node.selector.name; | 2811 String opname = node.selector.name; |
| 2822 if (node.dartArgumentsLength == 0) { | 2812 if (node.argumentRefs.length == 0) { |
| 2823 // Unary operator. | 2813 // Unary operator. |
| 2824 if (opname == "unary-") { | 2814 if (opname == "unary-") { |
| 2825 opname = "-"; | 2815 opname = "-"; |
| 2826 } | 2816 } |
| 2827 UnaryOperator operator = UnaryOperator.parse(opname); | 2817 UnaryOperator operator = UnaryOperator.parse(opname); |
| 2828 AbstractConstantValue result = lattice.unaryOp(operator, receiver); | 2818 AbstractConstantValue result = lattice.unaryOp(operator, receiver); |
| 2829 return finish(result, canReplace: !receiver.isNullable); | 2819 return finish(result, canReplace: !receiver.isNullable); |
| 2830 } else if (node.dartArgumentsLength == 1) { | 2820 } else if (node.argumentRefs.length == 1) { |
| 2831 // Binary operator. | 2821 // Binary operator. |
| 2832 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2822 AbstractConstantValue right = getValue(node.argument(0)); |
| 2833 BinaryOperator operator = BinaryOperator.parse(opname); | 2823 BinaryOperator operator = BinaryOperator.parse(opname); |
| 2834 AbstractConstantValue result = | 2824 AbstractConstantValue result = |
| 2835 lattice.binaryOp(operator, receiver, right); | 2825 lattice.binaryOp(operator, receiver, right); |
| 2836 return finish(result, canReplace: !receiver.isNullable); | 2826 return finish(result, canReplace: !receiver.isNullable); |
| 2837 } | 2827 } |
| 2838 return finish(null); | 2828 return finish(null); |
| 2839 } | 2829 } |
| 2840 | 2830 |
| 2841 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 2831 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 2842 | 2832 |
| (...skipping 714 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3557 } | 3547 } |
| 3558 | 3548 |
| 3559 Primitive makeCheck(CpsFragment cps, Primitive value) { | 3549 Primitive makeCheck(CpsFragment cps, Primitive value) { |
| 3560 return cps.applyBuiltin(negatedOperator, [value]); | 3550 return cps.applyBuiltin(negatedOperator, [value]); |
| 3561 } | 3551 } |
| 3562 | 3552 |
| 3563 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { | 3553 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { |
| 3564 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); | 3554 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); |
| 3565 } | 3555 } |
| 3566 } | 3556 } |
| OLD | NEW |