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