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 |