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