| 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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 new SsaDeadPhiEliminator(), | 51 new SsaDeadPhiEliminator(), |
| 52 new SsaConstantFolder(constantSystem, backend, work, types), | 52 new SsaConstantFolder(constantSystem, backend, work, types), |
| 53 new SsaTypePropagator(compiler, types), | 53 new SsaTypePropagator(compiler, types), |
| 54 new SsaReceiverSpecialization(compiler), | 54 new SsaReceiverSpecialization(compiler), |
| 55 new SsaGlobalValueNumberer(compiler, types), | 55 new SsaGlobalValueNumberer(compiler, types), |
| 56 new SsaCodeMotion(), | 56 new SsaCodeMotion(), |
| 57 new SsaValueRangeAnalyzer(constantSystem, types, work), | 57 new SsaValueRangeAnalyzer(constantSystem, types, work), |
| 58 // Previous optimizations may have generated new | 58 // Previous optimizations may have generated new |
| 59 // opportunities for constant folding. | 59 // opportunities for constant folding. |
| 60 new SsaConstantFolder(constantSystem, backend, work, types), | 60 new SsaConstantFolder(constantSystem, backend, work, types), |
| 61 new SsaSimplifyInterceptors(constantSystem), |
| 61 new SsaDeadCodeEliminator(types)]; | 62 new SsaDeadCodeEliminator(types)]; |
| 62 runPhases(graph, phases); | 63 runPhases(graph, phases); |
| 63 if (!speculative) { | 64 if (!speculative) { |
| 64 runPhase(graph, new SsaConstructionFieldTypes(backend, work, types)); | 65 runPhase(graph, new SsaConstructionFieldTypes(backend, work, types)); |
| 65 } | 66 } |
| 66 }); | 67 }); |
| 67 } | 68 } |
| 68 | 69 |
| 69 bool trySpeculativeOptimizations(CodegenWorkItem work, HGraph graph) { | 70 bool trySpeculativeOptimizations(CodegenWorkItem work, HGraph graph) { |
| 70 if (work.element.isField()) { | 71 if (work.element.isField()) { |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 | 213 |
| 213 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { | 214 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { |
| 214 if (operand is HConstant) { | 215 if (operand is HConstant) { |
| 215 HConstant receiver = operand; | 216 HConstant receiver = operand; |
| 216 Constant folded = operation.fold(receiver.constant); | 217 Constant folded = operation.fold(receiver.constant); |
| 217 if (folded != null) return graph.addConstant(folded); | 218 if (folded != null) return graph.addConstant(folded); |
| 218 } | 219 } |
| 219 return null; | 220 return null; |
| 220 } | 221 } |
| 221 | 222 |
| 222 HInstruction handleInterceptorCall(HInvokeDynamicMethod node) { | 223 HInstruction handleInterceptorCall(HInvokeDynamic node) { |
| 224 // We only optimize for intercepted method calls in this method. |
| 225 if (node.selector.isGetter() || node.selector.isSetter()) return node; |
| 226 |
| 223 HInstruction input = node.inputs[1]; | 227 HInstruction input = node.inputs[1]; |
| 224 if (input.isString(types) | 228 if (input.isString(types) |
| 225 && node.selector.name == const SourceString('toString')) { | 229 && node.selector.name == const SourceString('toString')) { |
| 226 return node.inputs[1]; | 230 return node.inputs[1]; |
| 227 } | 231 } |
| 228 | 232 |
| 229 // Try constant folding the instruction. | 233 // Try constant folding the instruction. |
| 230 Operation operation = node.specializer.operation(constantSystem); | 234 Operation operation = node.specializer.operation(constantSystem); |
| 231 if (operation != null) { | 235 if (operation != null) { |
| 232 HInstruction instruction = node.inputs.length == 2 | 236 HInstruction instruction = node.inputs.length == 2 |
| 233 ? foldUnary(operation, node.inputs[1]) | 237 ? foldUnary(operation, node.inputs[1]) |
| 234 : foldBinary(operation, node.inputs[1], node.inputs[2]); | 238 : foldBinary(operation, node.inputs[1], node.inputs[2]); |
| 235 if (instruction != null) return instruction; | 239 if (instruction != null) return instruction; |
| 236 } | 240 } |
| 237 | 241 |
| 238 // Try converting the instruction to a builtin instruction. | 242 // Try converting the instruction to a builtin instruction. |
| 239 HInstruction instruction = | 243 HInstruction instruction = |
| 240 node.specializer.tryConvertToBuiltin(node, types); | 244 node.specializer.tryConvertToBuiltin(node, types); |
| 241 if (instruction != null) return instruction; | 245 if (instruction != null) return instruction; |
| 242 | 246 |
| 243 // Check if this call does not need to be intercepted. | 247 // Check if this call does not need to be intercepted. |
| 244 HType type = types[input]; | 248 HType type = types[input]; |
| 245 var interceptor = node.inputs[0]; | 249 var interceptor = node.inputs[0]; |
| 246 if (interceptor is !HThis && !type.canBePrimitive()) { | 250 if (interceptor is !HThis && !type.canBePrimitive()) { |
| 247 // If the type can be null, and the intercepted method can be in | 251 // If the type can be null, and the intercepted method can be in |
| 248 // the object class, keep the interceptor. | 252 // the object class, keep the interceptor. |
| 249 if (type.canBeNull() | 253 if (type.canBeNull()) { |
| 250 && interceptor.interceptedClasses.contains(compiler.objectClass)) { | 254 Set<ClassElement> interceptedClasses; |
| 251 return node; | 255 if (interceptor is HInterceptor) { |
| 256 interceptedClasses = interceptor.interceptedClasses; |
| 257 } else if (node is HOneShotInterceptor) { |
| 258 var oneShotInterceptor = node; |
| 259 interceptedClasses = oneShotInterceptor.interceptedClasses; |
| 260 } |
| 261 if (interceptedClasses.contains(compiler.objectClass)) return node; |
| 252 } | 262 } |
| 253 // Change the call to a regular invoke dynamic call. | 263 // Change the call to a regular invoke dynamic call. |
| 254 return new HInvokeDynamicMethod( | 264 return new HInvokeDynamicMethod( |
| 255 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); | 265 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| 256 } | 266 } |
| 257 | 267 |
| 258 Selector selector = node.selector; | 268 Selector selector = node.selector; |
| 259 SourceString selectorName = selector.name; | 269 SourceString selectorName = selector.name; |
| 260 Element target; | 270 Element target; |
| 261 if (input.isExtendableArray(types)) { | 271 if (input.isExtendableArray(types)) { |
| (...skipping 400 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 662 HConstant constant = part; | 672 HConstant constant = part; |
| 663 if (!constant.constant.isPrimitive()) return node; | 673 if (!constant.constant.isPrimitive()) return node; |
| 664 PrimitiveConstant primitive = constant.constant; | 674 PrimitiveConstant primitive = constant.constant; |
| 665 folded = new DartString.concat(folded, primitive.toDartString()); | 675 folded = new DartString.concat(folded, primitive.toDartString()); |
| 666 } | 676 } |
| 667 return graph.addConstant(constantSystem.createString(folded, node.node)); | 677 return graph.addConstant(constantSystem.createString(folded, node.node)); |
| 668 } | 678 } |
| 669 | 679 |
| 670 HInstruction visitInterceptor(HInterceptor node) { | 680 HInstruction visitInterceptor(HInterceptor node) { |
| 671 if (node.isConstant()) return node; | 681 if (node.isConstant()) return node; |
| 672 HType type = types[node.inputs[0]]; | 682 HInstruction constant = tryComputeConstantInterceptor( |
| 683 node.inputs[0], node.interceptedClasses); |
| 684 if (constant == null) return node; |
| 685 return constant; |
| 686 } |
| 687 |
| 688 HInstruction tryComputeConstantInterceptor(HInstruction input, |
| 689 Set<ClassElement> intercepted) { |
| 690 HType type = types[input]; |
| 673 ClassElement constantInterceptor; | 691 ClassElement constantInterceptor; |
| 674 if (type.isInteger()) { | 692 if (type.isInteger()) { |
| 675 constantInterceptor = backend.jsIntClass; | 693 constantInterceptor = backend.jsIntClass; |
| 676 } else if (type.isDouble()) { | 694 } else if (type.isDouble()) { |
| 677 constantInterceptor = backend.jsDoubleClass; | 695 constantInterceptor = backend.jsDoubleClass; |
| 678 } else if (type.isBoolean()) { | 696 } else if (type.isBoolean()) { |
| 679 constantInterceptor = backend.jsBoolClass; | 697 constantInterceptor = backend.jsBoolClass; |
| 680 } else if (type.isString()) { | 698 } else if (type.isString()) { |
| 681 constantInterceptor = backend.jsStringClass; | 699 constantInterceptor = backend.jsStringClass; |
| 682 } else if (type.isArray()) { | 700 } else if (type.isArray()) { |
| 683 constantInterceptor = backend.jsArrayClass; | 701 constantInterceptor = backend.jsArrayClass; |
| 684 } else if (type.isNull()) { | 702 } else if (type.isNull()) { |
| 685 constantInterceptor = backend.jsIntClass; | 703 constantInterceptor = backend.jsIntClass; |
| 686 } else if (type.isNumber()) { | 704 } else if (type.isNumber()) { |
| 687 Set<ClassElement> intercepted = node.interceptedClasses; | |
| 688 // If the method being intercepted is not defined in [int] or | 705 // If the method being intercepted is not defined in [int] or |
| 689 // [double] we can safely use the number interceptor. | 706 // [double] we can safely use the number interceptor. |
| 690 if (!intercepted.contains(compiler.intClass) | 707 if (!intercepted.contains(compiler.intClass) |
| 691 && !intercepted.contains(compiler.doubleClass)) { | 708 && !intercepted.contains(compiler.doubleClass)) { |
| 692 constantInterceptor = backend.jsNumberClass; | 709 constantInterceptor = backend.jsNumberClass; |
| 693 } | 710 } |
| 694 } | 711 } |
| 695 | 712 |
| 696 if (constantInterceptor == null) return node; | 713 if (constantInterceptor == null) return null; |
| 697 if (constantInterceptor == work.element.getEnclosingClass()) { | 714 if (constantInterceptor == work.element.getEnclosingClass()) { |
| 698 return graph.thisInstruction; | 715 return graph.thisInstruction; |
| 699 } | 716 } |
| 700 | 717 |
| 701 Constant constant = new ConstructedConstant( | 718 Constant constant = new ConstructedConstant( |
| 702 constantInterceptor.computeType(compiler), <Constant>[]); | 719 constantInterceptor.computeType(compiler), <Constant>[]); |
| 703 return graph.addConstant(constant); | 720 return graph.addConstant(constant); |
| 704 } | 721 } |
| 722 |
| 723 HInstruction visitOneShotInterceptor(HOneShotInterceptor node) { |
| 724 HInstruction newInstruction = handleInterceptorCall(node); |
| 725 if (newInstruction != null) return newInstruction; |
| 726 |
| 727 HInstruction constant = tryComputeConstantInterceptor( |
| 728 node.inputs[1], node.interceptedClasses); |
| 729 |
| 730 if (constant == null) return node; |
| 731 |
| 732 Selector selector = node.selector; |
| 733 // TODO(ngeoffray): make one shot interceptors know whether |
| 734 // they have side effects. |
| 735 if (selector.isGetter()) { |
| 736 HInstruction res = new HInvokeDynamicGetter( |
| 737 selector, node.element, constant, false); |
| 738 res.inputs.add(node.intputs[1]); |
| 739 return res; |
| 740 } else if (node.selector.isSetter()) { |
| 741 HInstruction res = new HInvokeDynamicSetter( |
| 742 selector, node.element, constant, node.inputs[1], false); |
| 743 res.inputs.add(node.intputs[2]); |
| 744 return res; |
| 745 } else { |
| 746 List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); |
| 747 inputs[0] = constant; |
| 748 return new HInvokeDynamicMethod(selector, inputs, true); |
| 749 } |
| 750 } |
| 705 } | 751 } |
| 706 | 752 |
| 707 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { | 753 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { |
| 708 final HTypeMap types; | 754 final HTypeMap types; |
| 709 final Set<HInstruction> boundsChecked; | 755 final Set<HInstruction> boundsChecked; |
| 710 final CodegenWorkItem work; | 756 final CodegenWorkItem work; |
| 711 final JavaScriptBackend backend; | 757 final JavaScriptBackend backend; |
| 712 final String name = "SsaCheckInserter"; | 758 final String name = "SsaCheckInserter"; |
| 713 HGraph graph; | 759 HGraph graph; |
| 714 | 760 |
| (...skipping 723 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1438 || otherIntercepted.contains(backend.jsDoubleClass)) { | 1484 || otherIntercepted.contains(backend.jsDoubleClass)) { |
| 1439 interceptor.interceptedClasses.addAll(user.interceptedClasses); | 1485 interceptor.interceptedClasses.addAll(user.interceptedClasses); |
| 1440 } | 1486 } |
| 1441 user.interceptedClasses = interceptor.interceptedClasses; | 1487 user.interceptedClasses = interceptor.interceptedClasses; |
| 1442 } | 1488 } |
| 1443 } | 1489 } |
| 1444 } | 1490 } |
| 1445 | 1491 |
| 1446 // TODO(ngeoffray): Also implement it for non-intercepted calls. | 1492 // TODO(ngeoffray): Also implement it for non-intercepted calls. |
| 1447 } | 1493 } |
| 1494 |
| 1495 /** |
| 1496 * This phase replaces all interceptors that are used only once with |
| 1497 * one-shot interceptors. It saves code size and makes the receiver of |
| 1498 * an intercepted call a candidate for being generated at use site. |
| 1499 */ |
| 1500 class SsaSimplifyInterceptors extends HBaseVisitor |
| 1501 implements OptimizationPhase { |
| 1502 final String name = "SsaSimplifyInterceptors"; |
| 1503 final ConstantSystem constantSystem; |
| 1504 HGraph graph; |
| 1505 |
| 1506 SsaSimplifyInterceptors(this.constantSystem); |
| 1507 |
| 1508 void visitGraph(HGraph graph) { |
| 1509 this.graph = graph; |
| 1510 visitDominatorTree(graph); |
| 1511 } |
| 1512 |
| 1513 void visitInterceptor(HInterceptor node) { |
| 1514 if (node.usedBy.length != 1) return; |
| 1515 // [HBailoutTarget] instructions might have the interceptor as |
| 1516 // input. In such situation we let the dead code analyzer find out |
| 1517 // the interceptor is not needed. |
| 1518 if (node.usedBy[0] is !HInvokeDynamic) return; |
| 1519 |
| 1520 HInvokeDynamic user = node.usedBy[0]; |
| 1521 |
| 1522 // If [node] was loop hoisted, we keep the interceptor. |
| 1523 if (!user.hasSameLoopHeaderAs(node)) return; |
| 1524 |
| 1525 // Replace the user with a [HOneShotInterceptor]. |
| 1526 HConstant nullConstant = graph.addConstantNull(constantSystem); |
| 1527 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |
| 1528 inputs[0] = nullConstant; |
| 1529 HOneShotInterceptor interceptor = new HOneShotInterceptor( |
| 1530 user.selector, inputs, node.interceptedClasses); |
| 1531 interceptor.sourcePosition = user.sourcePosition; |
| 1532 |
| 1533 HBasicBlock block = user.block; |
| 1534 block.addAfter(user, interceptor); |
| 1535 block.rewrite(user, interceptor); |
| 1536 block.remove(user); |
| 1537 |
| 1538 // The interceptor will be removed in the dead code elimination |
| 1539 // phase. Note that removing it here would not work because of how |
| 1540 // the [visitBasicBlock] is implemented. |
| 1541 } |
| 1542 } |
| OLD | NEW |