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