| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 abstract class OptimizationPhase { | 7 abstract class OptimizationPhase { |
| 8 String get name; | 8 String get name; |
| 9 void visitGraph(HGraph graph); | 9 void visitGraph(HGraph graph); |
| 10 } | 10 } |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 : foldBinary(operation, node.inputs[1], node.inputs[2]); | 294 : foldBinary(operation, node.inputs[1], node.inputs[2]); |
| 295 if (instruction != null) return instruction; | 295 if (instruction != null) return instruction; |
| 296 } | 296 } |
| 297 | 297 |
| 298 // Try converting the instruction to a builtin instruction. | 298 // Try converting the instruction to a builtin instruction. |
| 299 HInstruction instruction = | 299 HInstruction instruction = |
| 300 node.specializer.tryConvertToBuiltin(node, compiler); | 300 node.specializer.tryConvertToBuiltin(node, compiler); |
| 301 if (instruction != null) return instruction; | 301 if (instruction != null) return instruction; |
| 302 | 302 |
| 303 Selector selector = node.selector; | 303 Selector selector = node.selector; |
| 304 TypeMask mask = node.mask; |
| 304 HInstruction input = node.inputs[1]; | 305 HInstruction input = node.inputs[1]; |
| 305 | 306 |
| 306 World world = compiler.world; | 307 World world = compiler.world; |
| 308 |
| 309 bool applies(Element element) { |
| 310 return selector.applies(element, world) && |
| 311 (mask == null || mask.canHit(element, selector, world)); |
| 312 } |
| 313 |
| 307 if (selector.isCall || selector.isOperator) { | 314 if (selector.isCall || selector.isOperator) { |
| 308 Element target; | 315 Element target; |
| 309 if (input.isExtendableArray(compiler)) { | 316 if (input.isExtendableArray(compiler)) { |
| 310 if (selector.applies(backend.jsArrayRemoveLast, world)) { | 317 if (applies(backend.jsArrayRemoveLast)) { |
| 311 target = backend.jsArrayRemoveLast; | 318 target = backend.jsArrayRemoveLast; |
| 312 } else if (selector.applies(backend.jsArrayAdd, world)) { | 319 } else if (applies(backend.jsArrayAdd)) { |
| 313 // The codegen special cases array calls, but does not | 320 // The codegen special cases array calls, but does not |
| 314 // inline argument type checks. | 321 // inline argument type checks. |
| 315 if (!compiler.enableTypeAssertions) { | 322 if (!compiler.enableTypeAssertions) { |
| 316 target = backend.jsArrayAdd; | 323 target = backend.jsArrayAdd; |
| 317 } | 324 } |
| 318 } | 325 } |
| 319 } else if (input.isStringOrNull(compiler)) { | 326 } else if (input.isStringOrNull(compiler)) { |
| 320 if (selector.applies(backend.jsStringSplit, world)) { | 327 if (applies(backend.jsStringSplit)) { |
| 321 HInstruction argument = node.inputs[2]; | 328 HInstruction argument = node.inputs[2]; |
| 322 if (argument.isString(compiler)) { | 329 if (argument.isString(compiler)) { |
| 323 target = backend.jsStringSplit; | 330 target = backend.jsStringSplit; |
| 324 } | 331 } |
| 325 } else if (selector.applies(backend.jsStringOperatorAdd, world)) { | 332 } else if (applies(backend.jsStringOperatorAdd)) { |
| 326 // `operator+` is turned into a JavaScript '+' so we need to | 333 // `operator+` is turned into a JavaScript '+' so we need to |
| 327 // make sure the receiver and the argument are not null. | 334 // make sure the receiver and the argument are not null. |
| 328 // TODO(sra): Do this via [node.specializer]. | 335 // TODO(sra): Do this via [node.specializer]. |
| 329 HInstruction argument = node.inputs[2]; | 336 HInstruction argument = node.inputs[2]; |
| 330 if (argument.isString(compiler) | 337 if (argument.isString(compiler) |
| 331 && !input.canBeNull()) { | 338 && !input.canBeNull()) { |
| 332 return new HStringConcat(input, argument, null, | 339 return new HStringConcat(input, argument, null, |
| 333 node.instructionType); | 340 node.instructionType); |
| 334 } | 341 } |
| 335 } else if (selector.applies(backend.jsStringToString, world) | 342 } else if (applies(backend.jsStringToString) |
| 336 && !input.canBeNull()) { | 343 && !input.canBeNull()) { |
| 337 return input; | 344 return input; |
| 338 } | 345 } |
| 339 } | 346 } |
| 340 if (target != null) { | 347 if (target != null) { |
| 341 // TODO(ngeoffray): There is a strong dependency between codegen | 348 // TODO(ngeoffray): There is a strong dependency between codegen |
| 342 // and this optimization that the dynamic invoke does not need an | 349 // and this optimization that the dynamic invoke does not need an |
| 343 // interceptor. We currently need to keep a | 350 // interceptor. We currently need to keep a |
| 344 // HInvokeDynamicMethod and not create a HForeign because | 351 // HInvokeDynamicMethod and not create a HForeign because |
| 345 // HForeign is too opaque for the SsaCheckInserter (that adds a | 352 // HForeign is too opaque for the SsaCheckInserter (that adds a |
| 346 // bounds check on removeLast). Once we start inlining, the | 353 // bounds check on removeLast). Once we start inlining, the |
| 347 // bounds check will become explicit, so we won't need this | 354 // bounds check will become explicit, so we won't need this |
| 348 // optimization. | 355 // optimization. |
| 349 HInvokeDynamicMethod result = new HInvokeDynamicMethod( | 356 HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| 350 node.selector, node.inputs.sublist(1), node.instructionType); | 357 node.selector, node.mask, |
| 358 node.inputs.sublist(1), node.instructionType); |
| 351 result.element = target; | 359 result.element = target; |
| 352 return result; | 360 return result; |
| 353 } | 361 } |
| 354 } else if (selector.isGetter) { | 362 } else if (selector.isGetter) { |
| 355 if (selector.asUntyped.applies(backend.jsIndexableLength, world)) { | 363 if (selector.applies(backend.jsIndexableLength, world)) { |
| 356 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); | 364 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); |
| 357 if (optimized != null) return optimized; | 365 if (optimized != null) return optimized; |
| 358 } | 366 } |
| 359 } | 367 } |
| 360 | 368 |
| 361 return node; | 369 return node; |
| 362 } | 370 } |
| 363 | 371 |
| 364 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 372 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 365 if (node.isInterceptedCall) { | 373 if (node.isInterceptedCall) { |
| 366 HInstruction folded = handleInterceptedCall(node); | 374 HInstruction folded = handleInterceptedCall(node); |
| 367 if (folded != node) return folded; | 375 if (folded != node) return folded; |
| 368 } | 376 } |
| 369 | 377 |
| 370 TypeMask receiverType = node.getDartReceiver(compiler).instructionType; | 378 TypeMask receiverType = node.getDartReceiver(compiler).instructionType; |
| 371 Selector selector = | 379 Element element = |
| 372 new TypedSelector(receiverType, node.selector, compiler.world); | 380 compiler.world.locateSingleElement(node.selector, receiverType); |
| 373 Element element = compiler.world.locateSingleElement(selector); | |
| 374 // TODO(ngeoffray): Also fold if it's a getter or variable. | 381 // TODO(ngeoffray): Also fold if it's a getter or variable. |
| 375 if (element != null | 382 if (element != null |
| 376 && element.isFunction | 383 && element.isFunction |
| 377 // If we found out that the only target is a [:noSuchMethod:], | 384 // If we found out that the only target is a [:noSuchMethod:], |
| 378 // we just ignore it. | 385 // we just ignore it. |
| 379 && element.name == selector.name) { | 386 && element.name == node.selector.name) { |
| 380 FunctionElement method = element; | 387 FunctionElement method = element; |
| 381 | 388 |
| 382 if (method.isNative) { | 389 if (method.isNative) { |
| 383 HInstruction folded = tryInlineNativeMethod(node, method); | 390 HInstruction folded = tryInlineNativeMethod(node, method); |
| 384 if (folded != null) return folded; | 391 if (folded != null) return folded; |
| 385 } else { | 392 } else { |
| 386 // TODO(ngeoffray): If the method has optional parameters, | 393 // TODO(ngeoffray): If the method has optional parameters, |
| 387 // we should pass the default values. | 394 // we should pass the default values. |
| 388 FunctionSignature parameters = method.functionSignature; | 395 FunctionSignature parameters = method.functionSignature; |
| 389 if (parameters.optionalParameterCount == 0 | 396 if (parameters.optionalParameterCount == 0 || |
| 390 || parameters.parameterCount == node.selector.argumentCount) { | 397 parameters.parameterCount == |
| 398 node.selector.argumentCount) { |
| 391 node.element = element; | 399 node.element = element; |
| 392 } | 400 } |
| 393 } | 401 } |
| 394 } | 402 } |
| 395 return node; | 403 return node; |
| 396 } | 404 } |
| 397 | 405 |
| 398 HInstruction tryInlineNativeMethod(HInvokeDynamicMethod node, | 406 HInstruction tryInlineNativeMethod(HInvokeDynamicMethod node, |
| 399 FunctionElement method) { | 407 FunctionElement method) { |
| 400 // Enable direct calls to a native method only if we don't run in checked | 408 // Enable direct calls to a native method only if we don't run in checked |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 443 | 451 |
| 444 if (!canInline) return null; | 452 if (!canInline) return null; |
| 445 | 453 |
| 446 // Strengthen instruction type from annotations to help optimize | 454 // Strengthen instruction type from annotations to help optimize |
| 447 // dependent instructions. | 455 // dependent instructions. |
| 448 native.NativeBehavior nativeBehavior = | 456 native.NativeBehavior nativeBehavior = |
| 449 native.NativeBehavior.ofMethod(method, compiler); | 457 native.NativeBehavior.ofMethod(method, compiler); |
| 450 TypeMask returnType = | 458 TypeMask returnType = |
| 451 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); | 459 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); |
| 452 HInvokeDynamicMethod result = | 460 HInvokeDynamicMethod result = |
| 453 new HInvokeDynamicMethod(node.selector, inputs, returnType); | 461 new HInvokeDynamicMethod(node.selector, node.mask, inputs, returnType); |
| 454 result.element = method; | 462 result.element = method; |
| 455 return result; | 463 return result; |
| 456 } | 464 } |
| 457 | 465 |
| 458 HInstruction visitBoundsCheck(HBoundsCheck node) { | 466 HInstruction visitBoundsCheck(HBoundsCheck node) { |
| 459 HInstruction index = node.index; | 467 HInstruction index = node.index; |
| 460 if (index.isInteger(compiler)) return node; | 468 if (index.isInteger(compiler)) return node; |
| 461 if (index.isConstant()) { | 469 if (index.isConstant()) { |
| 462 HConstant constantInstruction = index; | 470 HConstant constantInstruction = index; |
| 463 assert(!constantInstruction.constant.isInt); | 471 assert(!constantInstruction.constant.isInt); |
| (...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 722 ClassWorld classWorld = compiler.world; | 730 ClassWorld classWorld = compiler.world; |
| 723 if (checkedType.containsAll(classWorld)) return node; | 731 if (checkedType.containsAll(classWorld)) return node; |
| 724 HInstruction input = node.checkedInput; | 732 HInstruction input = node.checkedInput; |
| 725 TypeMask inputType = input.instructionType; | 733 TypeMask inputType = input.instructionType; |
| 726 return inputType.isInMask(checkedType, classWorld) ? input : node; | 734 return inputType.isInMask(checkedType, classWorld) ? input : node; |
| 727 } | 735 } |
| 728 | 736 |
| 729 VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver, | 737 VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver, |
| 730 Selector selector) { | 738 Selector selector) { |
| 731 TypeMask receiverType = receiver.instructionType; | 739 TypeMask receiverType = receiver.instructionType; |
| 732 return compiler.world.locateSingleField( | 740 return compiler.world.locateSingleField(selector, receiverType); |
| 733 new TypedSelector(receiverType, selector, compiler.world)); | |
| 734 } | 741 } |
| 735 | 742 |
| 736 HInstruction visitFieldGet(HFieldGet node) { | 743 HInstruction visitFieldGet(HFieldGet node) { |
| 737 if (node.isNullCheck) return node; | 744 if (node.isNullCheck) return node; |
| 738 var receiver = node.receiver; | 745 var receiver = node.receiver; |
| 739 if (node.element == backend.jsIndexableLength) { | 746 if (node.element == backend.jsIndexableLength) { |
| 740 JavaScriptItemCompilationContext context = work.compilationContext; | 747 JavaScriptItemCompilationContext context = work.compilationContext; |
| 741 if (context.allocatedFixedLists.contains(receiver)) { | 748 if (context.allocatedFixedLists.contains(receiver)) { |
| 742 // TODO(ngeoffray): checking if the second input is an integer | 749 // TODO(ngeoffray): checking if the second input is an integer |
| 743 // should not be necessary but it currently makes it easier for | 750 // should not be necessary but it currently makes it easier for |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 795 } | 802 } |
| 796 return node; | 803 return node; |
| 797 } | 804 } |
| 798 | 805 |
| 799 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | 806 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| 800 if (node.isInterceptedCall) { | 807 if (node.isInterceptedCall) { |
| 801 HInstruction folded = handleInterceptedCall(node); | 808 HInstruction folded = handleInterceptedCall(node); |
| 802 if (folded != node) return folded; | 809 if (folded != node) return folded; |
| 803 } | 810 } |
| 804 HInstruction receiver = node.getDartReceiver(compiler); | 811 HInstruction receiver = node.getDartReceiver(compiler); |
| 805 Element field = findConcreteFieldForDynamicAccess(receiver, node.selector); | 812 Element field = findConcreteFieldForDynamicAccess( |
| 813 receiver, node.selector); |
| 806 if (field == null) return node; | 814 if (field == null) return node; |
| 807 return directFieldGet(receiver, field); | 815 return directFieldGet(receiver, field); |
| 808 } | 816 } |
| 809 | 817 |
| 810 HInstruction directFieldGet(HInstruction receiver, Element field) { | 818 HInstruction directFieldGet(HInstruction receiver, Element field) { |
| 811 bool isAssignable = !compiler.world.fieldNeverChanges(field); | 819 bool isAssignable = !compiler.world.fieldNeverChanges(field); |
| 812 | 820 |
| 813 TypeMask type; | 821 TypeMask type; |
| 814 if (field.enclosingClass.isNative) { | 822 if (field.enclosingClass.isNative) { |
| 815 type = TypeMaskFactory.fromNativeBehavior( | 823 type = TypeMaskFactory.fromNativeBehavior( |
| (...skipping 1481 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2297 | 2305 |
| 2298 keyedValues.forEach((receiver, values) { | 2306 keyedValues.forEach((receiver, values) { |
| 2299 result.keyedValues[receiver] = | 2307 result.keyedValues[receiver] = |
| 2300 new Map<HInstruction, HInstruction>.from(values); | 2308 new Map<HInstruction, HInstruction>.from(values); |
| 2301 }); | 2309 }); |
| 2302 | 2310 |
| 2303 result.nonEscapingReceivers.addAll(nonEscapingReceivers); | 2311 result.nonEscapingReceivers.addAll(nonEscapingReceivers); |
| 2304 return result; | 2312 return result; |
| 2305 } | 2313 } |
| 2306 } | 2314 } |
| OLD | NEW |