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 |