| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library cps_ir.optimization.insert_refinements; | 5 library cps_ir.optimization.insert_refinements; |
| 6 | 6 |
| 7 import 'dart:math' show min; |
| 7 import 'optimizers.dart' show Pass; | 8 import 'optimizers.dart' show Pass; |
| 8 import 'cps_ir_nodes.dart'; | 9 import 'cps_ir_nodes.dart'; |
| 10 import '../elements/elements.dart'; |
| 9 import '../common/names.dart'; | 11 import '../common/names.dart'; |
| 10 import '../types/types.dart' show TypeMask; | 12 import '../types/types.dart' show TypeMask; |
| 13 import '../universe/selector.dart'; |
| 11 import 'type_mask_system.dart'; | 14 import 'type_mask_system.dart'; |
| 12 | 15 |
| 13 /// Inserts [Refinement] nodes in the IR to allow for sparse path-sensitive | 16 /// Inserts [Refinement] nodes in the IR to allow for sparse path-sensitive |
| 14 /// type analysis in the [TypePropagator] pass. | 17 /// type analysis in the [TypePropagator] pass. |
| 15 /// | 18 /// |
| 16 /// Refinement nodes are inserted at the arms of a [Branch] node with a | 19 /// Refinement nodes are inserted at the arms of a [Branch] node with a |
| 17 /// condition of form `x is T` or `x == null`. | 20 /// condition of form `x is T` or `x == null`. |
| 18 /// | 21 /// |
| 19 /// Refinement nodes are inserted after a method invocation to refine the | 22 /// Refinement nodes are inserted after a method invocation to refine the |
| 20 /// receiver to the types that can respond to the given selector. | 23 /// receiver to the types that can respond to the given selector. |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 | 100 |
| 98 /// Enqueues [cont] for processing in a context where [refined] is the | 101 /// Enqueues [cont] for processing in a context where [refined] is the |
| 99 /// current refinement for its value. | 102 /// current refinement for its value. |
| 100 void pushRefinement(Continuation cont, Refinement refined) { | 103 void pushRefinement(Continuation cont, Refinement refined) { |
| 101 pushAction(() { | 104 pushAction(() { |
| 102 applyRefinement(cont, refined); | 105 applyRefinement(cont, refined); |
| 103 push(cont); | 106 push(cont); |
| 104 }); | 107 }); |
| 105 } | 108 } |
| 106 | 109 |
| 110 /// Refine the type of each argument on [node] according to the provided |
| 111 /// type masks. |
| 112 void _refineArguments( |
| 113 InvocationPrimitive node, List<TypeMask> argumentSuccessTypes) { |
| 114 if (argumentSuccessTypes == null) return; |
| 115 |
| 116 // Note: node.dartArgumentsLength is shorter when the call doesn't include |
| 117 // some optional arguments. |
| 118 int length = min(argumentSuccessTypes.length, node.dartArgumentsLength); |
| 119 for (int i = 0; i < length; i++) { |
| 120 TypeMask argSuccessType = argumentSuccessTypes[i]; |
| 121 |
| 122 // Skip arguments that provide no refinement. |
| 123 if (argSuccessType == types.dynamicType) continue; |
| 124 |
| 125 applyRefinement(node.parent, |
| 126 new Refinement(node.dartArgument(i), argSuccessType)); |
| 127 } |
| 128 } |
| 129 |
| 130 void visitInvokeStatic(InvokeStatic node) { |
| 131 _refineArguments(node, |
| 132 _getSuccessTypesForStaticMethod(types, node.target)); |
| 133 } |
| 134 |
| 107 void visitInvokeMethod(InvokeMethod node) { | 135 void visitInvokeMethod(InvokeMethod node) { |
| 108 // Update references to their current refined values. | 136 // Update references to their current refined values. |
| 109 processReference(node.receiver); | 137 processReference(node.receiver); |
| 110 node.arguments.forEach(processReference); | 138 node.arguments.forEach(processReference); |
| 111 | 139 |
| 112 // If the call is intercepted, we want to refine the actual receiver, | 140 // If the call is intercepted, we want to refine the actual receiver, |
| 113 // not the interceptor. | 141 // not the interceptor. |
| 114 Primitive receiver = unfoldInterceptor(node.receiver.definition); | 142 Primitive receiver = unfoldInterceptor(node.receiver.definition); |
| 115 | 143 |
| 116 // Do not try to refine the receiver of closure calls; the class world | 144 // Do not try to refine the receiver of closure calls; the class world |
| 117 // does not know about closure classes. | 145 // does not know about closure classes. |
| 118 if (!node.selector.isClosureCall) { | 146 Selector selector = node.selector; |
| 147 if (!selector.isClosureCall) { |
| 119 // Filter away receivers that throw on this selector. | 148 // Filter away receivers that throw on this selector. |
| 120 TypeMask type = types.receiverTypeFor(node.selector, node.mask); | 149 TypeMask type = types.receiverTypeFor(selector, node.mask); |
| 121 Refinement refinement = new Refinement(receiver, type); | 150 Refinement refinement = new Refinement(receiver, type); |
| 122 LetPrim letPrim = node.parent; | 151 LetPrim letPrim = node.parent; |
| 123 applyRefinement(letPrim, refinement); | 152 applyRefinement(letPrim, refinement); |
| 153 |
| 154 // Refine arguments of methods on numbers which we know will throw on |
| 155 // invalid argument values. |
| 156 _refineArguments(node, |
| 157 _getSuccessTypesForInstanceMethod(types, type, selector)); |
| 124 } | 158 } |
| 125 } | 159 } |
| 126 | 160 |
| 127 void visitTypeCast(TypeCast node) { | 161 void visitTypeCast(TypeCast node) { |
| 128 Primitive value = node.value.definition; | 162 Primitive value = node.value.definition; |
| 129 | 163 |
| 130 processReference(node.value); | 164 processReference(node.value); |
| 131 node.typeArguments.forEach(processReference); | 165 node.typeArguments.forEach(processReference); |
| 132 | 166 |
| 133 // Refine the type of the input. | 167 // Refine the type of the input. |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 Expression traverseLetCont(LetCont node) { | 260 Expression traverseLetCont(LetCont node) { |
| 227 for (Continuation cont in node.continuations) { | 261 for (Continuation cont in node.continuations) { |
| 228 // Do not push the branch continuations here. visitBranch will do that. | 262 // Do not push the branch continuations here. visitBranch will do that. |
| 229 if (!(cont.hasExactlyOneUse && cont.firstRef.parent is Branch)) { | 263 if (!(cont.hasExactlyOneUse && cont.firstRef.parent is Branch)) { |
| 230 push(cont); | 264 push(cont); |
| 231 } | 265 } |
| 232 } | 266 } |
| 233 return node.body; | 267 return node.body; |
| 234 } | 268 } |
| 235 } | 269 } |
| 270 |
| 271 // TODO(sigmund): ideally this whitelist information should be stored as |
| 272 // metadata annotations on the runtime libraries so we can keep it in sync with |
| 273 // the implementation more easily. |
| 274 // TODO(sigmund): add support for constructors. |
| 275 // TODO(sigmund): add checks for RegExp and DateTime (currently not exposed as |
| 276 // easily in TypeMaskSystem). |
| 277 // TODO(sigmund): after the above TODOs are fixed, add: |
| 278 // ctor JSArray.fixed: [types.uint32Type], |
| 279 // ctor JSArray.growable: [types.uintType], |
| 280 // ctor DateTime': [int, int, int, int, int, int, int], |
| 281 // ctor DateTime.utc': [int, int, int, int, int, int, int], |
| 282 // ctor DateTime._internal': [int, int, int, int, int, int, int, bool], |
| 283 // ctor RegExp': [string, dynamic, dynamic], |
| 284 // method RegExp.allMatches: [string, int], |
| 285 // method RegExp.firstMatch: [string], |
| 286 // method RegExp.hasMatch: [string], |
| 287 List<TypeMask> _getSuccessTypesForInstanceMethod( |
| 288 TypeMaskSystem types, TypeMask receiver, Selector selector) { |
| 289 if (types.isDefinitelyInt(receiver)) { |
| 290 switch (selector.name) { |
| 291 case 'toSigned': |
| 292 case 'toUnsigned': |
| 293 case 'modInverse': |
| 294 case 'gcd': |
| 295 return [types.intType]; |
| 296 |
| 297 case 'modPow': |
| 298 return [types.intType, types.intType]; |
| 299 } |
| 300 // Note: num methods on int values are handled below. |
| 301 } |
| 302 |
| 303 if (types.isDefinitelyNum(receiver)) { |
| 304 switch (selector.name) { |
| 305 case 'clamp': |
| 306 return [types.numType, types.numType]; |
| 307 case 'toStringAsFixed': |
| 308 case 'toStringAsPrecision': |
| 309 case 'toRadixString': |
| 310 return [types.intType]; |
| 311 case 'toStringAsExponential': |
| 312 return [types.intType.nullable()]; |
| 313 case 'compareTo': |
| 314 case 'remainder': |
| 315 case '+': |
| 316 case '-': |
| 317 case '/': |
| 318 case '*': |
| 319 case '%': |
| 320 case '~/': |
| 321 case '<<': |
| 322 case '>>': |
| 323 case '&': |
| 324 case '|': |
| 325 case '^': |
| 326 case '<': |
| 327 case '>': |
| 328 case '<=': |
| 329 case '>=': |
| 330 return [types.numType]; |
| 331 default: |
| 332 return null; |
| 333 } |
| 334 } |
| 335 |
| 336 if (types.isDefinitelyString(receiver)) { |
| 337 switch (selector.name) { |
| 338 case 'allMatches': |
| 339 return [types.stringType, types.intType]; |
| 340 case 'endsWith': |
| 341 return [types.stringType]; |
| 342 case 'replaceAll': |
| 343 return [types.dynamicType, types.stringType]; |
| 344 case 'replaceFirst': |
| 345 return [types.dynamicType, types.stringType, types.intType]; |
| 346 case 'replaceFirstMapped': |
| 347 return [ |
| 348 types.dynamicType, |
| 349 types.dynamicType.nonNullable(), |
| 350 types.intType |
| 351 ]; |
| 352 case 'split': |
| 353 return [types.dynamicType.nonNullable()]; |
| 354 case 'replaceRange': |
| 355 return [types.intType, types.intType, types.stringType]; |
| 356 case 'startsWith': |
| 357 return [types.dynamicType, types.intType]; |
| 358 case 'substring': |
| 359 return [types.intType, types.uintType.nullable()]; |
| 360 case 'indexOf': |
| 361 return [types.dynamicType.nonNullable(), types.uintType]; |
| 362 case 'lastIndexOf': |
| 363 return [types.dynamicType.nonNullable(), types.uintType.nullable()]; |
| 364 case 'contains': |
| 365 return [ |
| 366 types.dynamicType.nonNullable(), |
| 367 // TODO(sigmund): update runtime to add check for int? |
| 368 types.dynamicType |
| 369 ]; |
| 370 case 'codeUnitAt': |
| 371 return [types.uintType]; |
| 372 case '+': |
| 373 return [types.stringType]; |
| 374 case '*': |
| 375 return [types.uint32Type]; |
| 376 case '[]': |
| 377 return [types.uintType]; |
| 378 default: |
| 379 return null; |
| 380 } |
| 381 } |
| 382 |
| 383 if (types.isDefinitelyArray(receiver)) { |
| 384 switch (selector.name) { |
| 385 case 'removeAt': |
| 386 case 'insert': |
| 387 return [types.uintType]; |
| 388 case 'sublist': |
| 389 return [types.uintType, types.uintType.nullable()]; |
| 390 case 'length': |
| 391 return selector.isSetter ? [types.uintType] : null; |
| 392 case '[]': |
| 393 case '[]=': |
| 394 return [types.uintType]; |
| 395 default: |
| 396 return null; |
| 397 } |
| 398 } |
| 399 return null; |
| 400 } |
| 401 |
| 402 List<TypeMask> _getSuccessTypesForStaticMethod( |
| 403 TypeMaskSystem types, FunctionElement target) { |
| 404 var lib = target.library; |
| 405 if (lib.isDartCore) { |
| 406 var cls = target.enclosingClass?.name; |
| 407 if (cls == 'int' && target.name == 'parse') { |
| 408 // source, onError, radix |
| 409 return [types.stringType, types.dynamicType, types.uint31Type.nullable()]; |
| 410 } else if (cls == 'double' && target.name == 'parse') { |
| 411 return [types.stringType, types.dynamicType]; |
| 412 } |
| 413 } |
| 414 |
| 415 if (lib.isPlatformLibrary && '${lib.canonicalUri}' == 'dart:math') { |
| 416 switch(target.name) { |
| 417 case 'sqrt': |
| 418 case 'sin': |
| 419 case 'cos': |
| 420 case 'tan': |
| 421 case 'acos': |
| 422 case 'asin': |
| 423 case 'atan': |
| 424 case 'atan2': |
| 425 case 'exp': |
| 426 case 'log': |
| 427 return [types.numType]; |
| 428 case 'pow': |
| 429 return [types.numType, types.numType]; |
| 430 } |
| 431 } |
| 432 |
| 433 return null; |
| 434 } |
| OLD | NEW |