| 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 26 matching lines...) Expand all Loading... |
| 37 // Run trivial constant folding first to optimize | 37 // Run trivial constant folding first to optimize |
| 38 // some patterns useful for type conversion. | 38 // some patterns useful for type conversion. |
| 39 new SsaConstantFolder(constantSystem, backend, work, types), | 39 new SsaConstantFolder(constantSystem, backend, work, types), |
| 40 new SsaTypeConversionInserter(compiler), | 40 new SsaTypeConversionInserter(compiler), |
| 41 new SsaTypePropagator(compiler, types), | 41 new SsaTypePropagator(compiler, types), |
| 42 new SsaCheckInserter(backend, work, types, context.boundsChecked), | 42 new SsaCheckInserter(backend, work, types, context.boundsChecked), |
| 43 new SsaConstantFolder(constantSystem, backend, work, types), | 43 new SsaConstantFolder(constantSystem, backend, work, types), |
| 44 new SsaRedundantPhiEliminator(), | 44 new SsaRedundantPhiEliminator(), |
| 45 new SsaDeadPhiEliminator(), | 45 new SsaDeadPhiEliminator(), |
| 46 new SsaConstantFolder(constantSystem, backend, work, types), | 46 new SsaConstantFolder(constantSystem, backend, work, types), |
| 47 new SsaTypePropagator(compiler, types), |
| 47 new SsaGlobalValueNumberer(compiler, types), | 48 new SsaGlobalValueNumberer(compiler, types), |
| 48 new SsaCodeMotion(), | 49 new SsaCodeMotion(), |
| 49 new SsaValueRangeAnalyzer(constantSystem, types, work), | 50 new SsaValueRangeAnalyzer(constantSystem, types, work), |
| 50 // Previous optimizations may have generated new | 51 // Previous optimizations may have generated new |
| 51 // opportunities for constant folding. | 52 // opportunities for constant folding. |
| 52 new SsaConstantFolder(constantSystem, backend, work, types), | 53 new SsaConstantFolder(constantSystem, backend, work, types), |
| 53 new SsaDeadCodeEliminator(types)]; | 54 new SsaDeadCodeEliminator(types)]; |
| 54 runPhases(graph, phases); | 55 runPhases(graph, phases); |
| 55 if (!speculative) { | 56 if (!speculative) { |
| 56 runPhase(graph, new SsaConstructionFieldTypes(backend, work, types)); | 57 runPhase(graph, new SsaConstructionFieldTypes(backend, work, types)); |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 HInstruction operand = node.operand; | 198 HInstruction operand = node.operand; |
| 198 if (operand is HConstant) { | 199 if (operand is HConstant) { |
| 199 UnaryOperation operation = node.operation(constantSystem); | 200 UnaryOperation operation = node.operation(constantSystem); |
| 200 HConstant receiver = operand; | 201 HConstant receiver = operand; |
| 201 Constant folded = operation.fold(receiver.constant); | 202 Constant folded = operation.fold(receiver.constant); |
| 202 if (folded != null) return graph.addConstant(folded); | 203 if (folded != null) return graph.addConstant(folded); |
| 203 } | 204 } |
| 204 return node; | 205 return node; |
| 205 } | 206 } |
| 206 | 207 |
| 207 HInstruction visitInvokeInterceptor(HInvokeInterceptor node) { | 208 HInstruction handleInterceptorCall(HInvokeDynamic node) { |
| 208 // Try to recognize the length interceptor with input [:new List(int):]. | |
| 209 if (node.isLengthGetter() && node.inputs[1] is HInvokeStatic) { | |
| 210 HInvokeStatic call = node.inputs[1]; | |
| 211 if (isFixedSizeListConstructor(call)) { | |
| 212 return call.inputs[1]; | |
| 213 } | |
| 214 } | |
| 215 HInstruction input = node.inputs[1]; | 209 HInstruction input = node.inputs[1]; |
| 216 if (node.isLengthGetter()) { | |
| 217 if (input.isConstantString()) { | |
| 218 HConstant constantInput = input; | |
| 219 StringConstant constant = constantInput.constant; | |
| 220 return graph.addConstantInt(constant.length, constantSystem); | |
| 221 } else if (input.isConstantList()) { | |
| 222 HConstant constantInput = input; | |
| 223 ListConstant constant = constantInput.constant; | |
| 224 return graph.addConstantInt(constant.length, constantSystem); | |
| 225 } else if (input.isConstantMap()) { | |
| 226 HConstant constantInput = input; | |
| 227 MapConstant constant = constantInput.constant; | |
| 228 return graph.addConstantInt(constant.length, constantSystem); | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 if (input.isString(types) | 210 if (input.isString(types) |
| 233 && node.selector.name == const SourceString('toString')) { | 211 && node.selector.name == const SourceString('toString')) { |
| 234 return node.inputs[1]; | 212 return node.inputs[1]; |
| 235 } | 213 } |
| 236 | |
| 237 if (!input.canBePrimitive(types) && node.selector.isCall()) { | |
| 238 bool transformToDynamicInvocation = true; | |
| 239 if (input.canBeNull(types)) { | |
| 240 // Check if the method exists on Null. If yes we must not transform | |
| 241 // the static interceptor call to a dynamic invocation. | |
| 242 // TODO(floitsch): get a list of methods that exist on 'null' and only | |
| 243 // bail out on them. | |
| 244 transformToDynamicInvocation = false; | |
| 245 } | |
| 246 if (transformToDynamicInvocation) { | |
| 247 return fromInterceptorToDynamicInvocation(node, node.selector); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 return node; | 214 return node; |
| 252 } | 215 } |
| 253 | 216 |
| 254 bool isFixedSizeListConstructor(HInvokeStatic node) { | 217 bool isFixedSizeListConstructor(HInvokeStatic node) { |
| 255 Element element = node.target.element; | 218 Element element = node.target.element; |
| 256 DartType defaultClass = compiler.listClass.defaultClass; | 219 DartType defaultClass = compiler.listClass.defaultClass; |
| 257 // TODO(ngeoffray): make sure that the only reason the List class is | 220 // TODO(ngeoffray): make sure that the only reason the List class is |
| 258 // not resolved is because it's not being used. | 221 // not resolved is because it's not being used. |
| 259 return element.isConstructor() | 222 return element.isConstructor() |
| 260 && defaultClass != null | 223 && defaultClass != null |
| 261 && element.enclosingElement.declaration == defaultClass.element | 224 && element.enclosingElement.declaration == defaultClass.element |
| 262 && node.inputs.length == 2 | 225 && node.inputs.length == 2 |
| 263 && node.inputs[1].isInteger(types); | 226 && node.inputs[1].isInteger(types); |
| 264 } | 227 } |
| 265 | 228 |
| 266 HInstruction visitInvokeStatic(HInvokeStatic node) { | 229 HInstruction visitInvokeStatic(HInvokeStatic node) { |
| 267 if (isFixedSizeListConstructor(node)) { | 230 if (isFixedSizeListConstructor(node)) { |
| 268 node.guaranteedType = HType.FIXED_ARRAY; | 231 node.guaranteedType = HType.FIXED_ARRAY; |
| 269 } | 232 } |
| 270 return node; | 233 return node; |
| 271 } | 234 } |
| 272 | 235 |
| 273 HInstruction visitInvokeDynamic(HInvokeDynamic node) { | 236 HInstruction visitInvokeDynamic(HInvokeDynamic node) { |
| 237 if (node.isInterceptorCall) return handleInterceptorCall(node); |
| 274 HType receiverType = types[node.receiver]; | 238 HType receiverType = types[node.receiver]; |
| 275 if (receiverType.isExact()) { | 239 if (receiverType.isExact()) { |
| 276 HBoundedType type = receiverType; | 240 HBoundedType type = receiverType; |
| 277 Element element = type.lookupMember(node.selector.name); | 241 Element element = type.lookupMember(node.selector.name); |
| 278 // TODO(ngeoffray): Also fold if it's a getter or variable. | 242 // TODO(ngeoffray): Also fold if it's a getter or variable. |
| 279 if (element != null && element.isFunction()) { | 243 if (element != null && element.isFunction()) { |
| 280 if (node.selector.applies(element, compiler)) { | 244 if (node.selector.applies(element, compiler)) { |
| 281 FunctionElement method = element; | 245 FunctionElement method = element; |
| 282 FunctionSignature parameters = method.computeSignature(compiler); | 246 FunctionSignature parameters = method.computeSignature(compiler); |
| 283 if (parameters.optionalParameterCount == 0) { | 247 if (parameters.optionalParameterCount == 0) { |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 585 Element findConcreteFieldForDynamicAccess(HInstruction receiver, | 549 Element findConcreteFieldForDynamicAccess(HInstruction receiver, |
| 586 Selector selector) { | 550 Selector selector) { |
| 587 HType receiverType = types[receiver]; | 551 HType receiverType = types[receiver]; |
| 588 if (!receiverType.isUseful()) return null; | 552 if (!receiverType.isUseful()) return null; |
| 589 if (receiverType.canBeNull()) return null; | 553 if (receiverType.canBeNull()) return null; |
| 590 DartType type = receiverType.computeType(compiler); | 554 DartType type = receiverType.computeType(compiler); |
| 591 if (type == null) return null; | 555 if (type == null) return null; |
| 592 return compiler.world.locateSingleField(type, selector); | 556 return compiler.world.locateSingleField(type, selector); |
| 593 } | 557 } |
| 594 | 558 |
| 559 HInstruction visitFieldGet(HFieldGet node) { |
| 560 if (node.element == backend.jsArrayLength) { |
| 561 if (node.receiver is HInvokeStatic) { |
| 562 // Try to recognize the length getter with input [:new List(int):]. |
| 563 HInvokeStatic call = node.receiver; |
| 564 if (isFixedSizeListConstructor(call)) { |
| 565 return call.inputs[1]; |
| 566 } |
| 567 } |
| 568 } |
| 569 return node; |
| 570 } |
| 571 |
| 572 HInstruction optimizeLengthInterceptedCall(HInvokeDynamicGetter node) { |
| 573 HInstruction actualReceiver = node.inputs[1]; |
| 574 if (actualReceiver.isIndexablePrimitive(types)) { |
| 575 if (actualReceiver.isConstantString()) { |
| 576 HConstant constantInput = actualReceiver; |
| 577 StringConstant constant = constantInput.constant; |
| 578 return graph.addConstantInt(constant.length, constantSystem); |
| 579 } else if (actualReceiver.isConstantList()) { |
| 580 HConstant constantInput = actualReceiver; |
| 581 ListConstant constant = constantInput.constant; |
| 582 return graph.addConstantInt(constant.length, constantSystem); |
| 583 } |
| 584 Element element; |
| 585 bool isAssignable; |
| 586 if (actualReceiver.isString(types)) { |
| 587 element = backend.jsStringLength; |
| 588 isAssignable = false; |
| 589 } else { |
| 590 element = backend.jsArrayLength; |
| 591 isAssignable = !actualReceiver.isFixedArray(types); |
| 592 } |
| 593 HFieldGet result = new HFieldGet( |
| 594 element, actualReceiver, isAssignable: isAssignable); |
| 595 result.guaranteedType = HType.INTEGER; |
| 596 types[result] = HType.INTEGER; |
| 597 return result; |
| 598 } else if (actualReceiver.isConstantMap()) { |
| 599 HConstant constantInput = actualReceiver; |
| 600 MapConstant constant = constantInput.constant; |
| 601 return graph.addConstantInt(constant.length, constantSystem); |
| 602 } |
| 603 return node; |
| 604 } |
| 605 |
| 595 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | 606 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| 607 if (node.selector.name == const SourceString('length') |
| 608 && node.isInterceptorCall) { |
| 609 return optimizeLengthInterceptedCall(node); |
| 610 } |
| 611 |
| 596 Element field = | 612 Element field = |
| 597 findConcreteFieldForDynamicAccess(node.receiver, node.selector); | 613 findConcreteFieldForDynamicAccess(node.receiver, node.selector); |
| 598 if (field == null) return node; | 614 if (field == null) return node; |
| 599 | 615 |
| 600 Modifiers modifiers = field.modifiers; | 616 Modifiers modifiers = field.modifiers; |
| 601 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); | 617 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); |
| 602 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { | 618 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { |
| 603 // If no setter is ever used for this field it is only initialized in the | 619 // If no setter is ever used for this field it is only initialized in the |
| 604 // initializer list. | 620 // initializer list. |
| 605 isFinalOrConst = true; | 621 isFinalOrConst = true; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 } | 664 } |
| 649 | 665 |
| 650 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { | 666 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { |
| 651 final HTypeMap types; | 667 final HTypeMap types; |
| 652 final ConstantSystem constantSystem; | 668 final ConstantSystem constantSystem; |
| 653 final Set<HInstruction> boundsChecked; | 669 final Set<HInstruction> boundsChecked; |
| 654 final WorkItem work; | 670 final WorkItem work; |
| 655 final String name = "SsaCheckInserter"; | 671 final String name = "SsaCheckInserter"; |
| 656 HGraph graph; | 672 HGraph graph; |
| 657 Element lengthInterceptor; | 673 Element lengthInterceptor; |
| 658 Selector lengthSelector; | |
| 659 | 674 |
| 660 SsaCheckInserter(JavaScriptBackend backend, | 675 SsaCheckInserter(JavaScriptBackend backend, |
| 661 this.work, | 676 this.work, |
| 662 this.types, | 677 this.types, |
| 663 this.boundsChecked) | 678 this.boundsChecked) |
| 664 : constantSystem = backend.constantSystem { | 679 : constantSystem = backend.constantSystem { |
| 665 SourceString lengthString = const SourceString('length'); | 680 lengthInterceptor = backend.jsArrayLength; |
| 666 lengthSelector = | |
| 667 new Selector.getter(lengthString, work.element.getLibrary()); | |
| 668 lengthInterceptor = | |
| 669 backend.builder.interceptors.getStaticInterceptor(lengthSelector); | |
| 670 } | 681 } |
| 671 | 682 |
| 672 void visitGraph(HGraph graph) { | 683 void visitGraph(HGraph graph) { |
| 673 this.graph = graph; | 684 this.graph = graph; |
| 674 visitDominatorTree(graph); | 685 visitDominatorTree(graph); |
| 675 } | 686 } |
| 676 | 687 |
| 677 void visitBasicBlock(HBasicBlock block) { | 688 void visitBasicBlock(HBasicBlock block) { |
| 678 HInstruction instruction = block.first; | 689 HInstruction instruction = block.first; |
| 679 while (instruction != null) { | 690 while (instruction != null) { |
| 680 HInstruction next = instruction.next; | 691 HInstruction next = instruction.next; |
| 681 instruction = instruction.accept(this); | 692 instruction = instruction.accept(this); |
| 682 instruction = next; | 693 instruction = next; |
| 683 } | 694 } |
| 684 } | 695 } |
| 685 | 696 |
| 686 HBoundsCheck insertBoundsCheck(HInstruction node, | 697 HBoundsCheck insertBoundsCheck(HInstruction node, |
| 687 HInstruction receiver, | 698 HInstruction receiver, |
| 688 HInstruction index) { | 699 HInstruction index) { |
| 689 HStatic interceptor = new HStatic(lengthInterceptor); | 700 bool isAssignable = !receiver.isFixedArray(types); |
| 690 node.block.addBefore(node, interceptor); | 701 HFieldGet length = |
| 691 HInvokeInterceptor length = new HInvokeInterceptor( | 702 new HFieldGet(lengthInterceptor, receiver, isAssignable: isAssignable); |
| 692 lengthSelector, <HInstruction>[interceptor, receiver], true); | 703 length.guaranteedType = HType.INTEGER; |
| 693 types[length] = HType.INTEGER; | 704 types[length] = HType.INTEGER; |
| 694 node.block.addBefore(node, length); | 705 node.block.addBefore(node, length); |
| 695 | 706 |
| 696 HBoundsCheck check = new HBoundsCheck(index, length); | 707 HBoundsCheck check = new HBoundsCheck(index, length); |
| 697 node.block.addBefore(node, check); | 708 node.block.addBefore(node, check); |
| 698 boundsChecked.add(node); | 709 boundsChecked.add(node); |
| 699 return check; | 710 return check; |
| 700 } | 711 } |
| 701 | 712 |
| 702 HIntegerCheck insertIntegerCheck(HInstruction node, HInstruction value) { | 713 HIntegerCheck insertIntegerCheck(HInstruction node, HInstruction value) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 725 if (!node.receiver.isMutableArray(types)) return; | 736 if (!node.receiver.isMutableArray(types)) return; |
| 726 if (boundsChecked.contains(node)) return; | 737 if (boundsChecked.contains(node)) return; |
| 727 HInstruction index = node.index; | 738 HInstruction index = node.index; |
| 728 if (!node.index.isInteger(types)) { | 739 if (!node.index.isInteger(types)) { |
| 729 index = insertIntegerCheck(node, index); | 740 index = insertIntegerCheck(node, index); |
| 730 } | 741 } |
| 731 index = insertBoundsCheck(node, node.receiver, index); | 742 index = insertBoundsCheck(node, node.receiver, index); |
| 732 node.changeUse(node.index, index); | 743 node.changeUse(node.index, index); |
| 733 assert(node.isBuiltin(types)); | 744 assert(node.isBuiltin(types)); |
| 734 } | 745 } |
| 735 | |
| 736 void visitInvokeInterceptor(HInvokeInterceptor node) { | |
| 737 if (!node.isPopCall(types)) return; | |
| 738 if (boundsChecked.contains(node)) return; | |
| 739 HInstruction receiver = node.inputs[1]; | |
| 740 insertBoundsCheck(node, receiver, graph.addConstantInt(0, constantSystem)); | |
| 741 } | |
| 742 } | 746 } |
| 743 | 747 |
| 744 class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { | 748 class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { |
| 745 final HTypeMap types; | 749 final HTypeMap types; |
| 746 final String name = "SsaDeadCodeEliminator"; | 750 final String name = "SsaDeadCodeEliminator"; |
| 747 | 751 |
| 748 SsaDeadCodeEliminator(this.types); | 752 SsaDeadCodeEliminator(this.types); |
| 749 | 753 |
| 750 bool isDeadCode(HInstruction instruction) { | 754 bool isDeadCode(HInstruction instruction) { |
| 751 return !instruction.hasSideEffects(types) | 755 return !instruction.hasSideEffects(types) |
| (...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1354 } | 1358 } |
| 1355 | 1359 |
| 1356 // For other fields having setters in the generative constructor body, set | 1360 // For other fields having setters in the generative constructor body, set |
| 1357 // the type to UNKNOWN to avoid relying on the type set in the initializer | 1361 // the type to UNKNOWN to avoid relying on the type set in the initializer |
| 1358 // list. | 1362 // list. |
| 1359 allSetters.forEach((Element element) { | 1363 allSetters.forEach((Element element) { |
| 1360 backend.registerFieldConstructor(element, HType.UNKNOWN); | 1364 backend.registerFieldConstructor(element, HType.UNKNOWN); |
| 1361 }); | 1365 }); |
| 1362 } | 1366 } |
| 1363 } | 1367 } |
| OLD | NEW |