| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library dart2js.cps_ir.let_sinking; | 5 library dart2js.cps_ir.loop_hierarchy; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import 'optimizers.dart'; | |
| 9 | |
| 10 /// Sinks single-use primitives to the use when this is safe and profitable. | |
| 11 /// | |
| 12 /// To avoid sinking non-constant primitives into loops, this pass performs a | |
| 13 /// control-flow analysis to determine the effective nesting of loops. | |
| 14 /// | |
| 15 /// In the example below, the value 'p' can be sunk to its use site in the | |
| 16 /// 'else' branch because that branch is not effectively part of a loop, | |
| 17 /// despite being lexically nested in a recursive continuation. | |
| 18 /// | |
| 19 /// let prim p = getInterceptor(<something>) | |
| 20 /// let rec kont x = | |
| 21 /// if (<loop condition>) | |
| 22 /// <loop body> | |
| 23 /// InvokeContinuation kont x' | |
| 24 /// else | |
| 25 /// <after loop> | |
| 26 /// return p.foo() | |
| 27 /// | |
| 28 class LetSinker extends RecursiveVisitor implements Pass { | |
| 29 String get passName => 'Let sinking'; | |
| 30 | |
| 31 LoopHierarchy loopHierarchy; | |
| 32 List<Continuation> stack = <Continuation>[]; | |
| 33 | |
| 34 /// Maps a sinkable primitive to its loop header. | |
| 35 Map<Primitive, Continuation> loopHeaderForPrimitive = | |
| 36 <Primitive, Continuation>{}; | |
| 37 | |
| 38 Continuation currentLoopHeader; | |
| 39 | |
| 40 void rewrite(FunctionDefinition node) { | |
| 41 new ParentVisitor().visit(node); | |
| 42 loopHierarchy = new LoopHierarchy(node); | |
| 43 visit(node.body); | |
| 44 } | |
| 45 | |
| 46 @override | |
| 47 Expression traverseLetPrim(LetPrim node) { | |
| 48 Primitive prim = node.primitive; | |
| 49 if (prim.hasExactlyOneUse && prim.isSafeForReordering) { | |
| 50 // This can potentially be sunk. Register the loop header, so when we | |
| 51 // find the use site, we can check if they are in the same loop. | |
| 52 loopHeaderForPrimitive[prim] = currentLoopHeader; | |
| 53 pushAction(() { | |
| 54 if (node.primitive != null) { | |
| 55 // The primitive could not be sunk. Try to sink dependencies here. | |
| 56 visit(node.primitive); | |
| 57 } else { | |
| 58 // The primitive was sunk. Destroy the old LetPrim. | |
| 59 InteriorNode parent = node.parent; | |
| 60 parent.body = node.body; | |
| 61 node.body.parent = parent; | |
| 62 } | |
| 63 }); | |
| 64 } else { | |
| 65 visit(node.primitive); | |
| 66 } | |
| 67 | |
| 68 // Visit the body, wherein this primitive may be sunk to its use site. | |
| 69 return node.body; | |
| 70 } | |
| 71 | |
| 72 @override | |
| 73 Expression traverseContinuation(Continuation cont) { | |
| 74 Continuation oldLoopHeader = currentLoopHeader; | |
| 75 pushAction(() { | |
| 76 currentLoopHeader = oldLoopHeader; | |
| 77 }); | |
| 78 currentLoopHeader = loopHierarchy.getLoopHeader(cont); | |
| 79 return cont.body; | |
| 80 } | |
| 81 | |
| 82 void processReference(Reference ref) { | |
| 83 Definition definition = ref.definition; | |
| 84 if (definition is Primitive && | |
| 85 definition is! Parameter && | |
| 86 definition.hasExactlyOneUse && | |
| 87 definition.isSafeForReordering) { | |
| 88 // Check if use is in the same loop. | |
| 89 Continuation bindingLoop = loopHeaderForPrimitive.remove(definition); | |
| 90 if (bindingLoop == currentLoopHeader || definition is Constant) { | |
| 91 // Sink the definition. | |
| 92 | |
| 93 Expression use = getEnclosingExpression(ref.parent); | |
| 94 LetPrim binding = definition.parent; | |
| 95 binding.primitive = null; // Mark old binding for deletion. | |
| 96 LetPrim newBinding = new LetPrim(definition); | |
| 97 definition.parent = newBinding; | |
| 98 InteriorNode useParent = use.parent; | |
| 99 useParent.body = newBinding; | |
| 100 newBinding.body = use; | |
| 101 use.parent = newBinding; | |
| 102 newBinding.parent = useParent; | |
| 103 | |
| 104 // Now that the final binding location has been found, sink the | |
| 105 // dependencies of the definition down here as well. | |
| 106 visit(definition); | |
| 107 } | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 Expression getEnclosingExpression(Node node) { | |
| 112 while (node is! Expression) { | |
| 113 node = node.parent; | |
| 114 } | |
| 115 return node; | |
| 116 } | |
| 117 } | |
| 118 | 8 |
| 119 /// Determines the effective nesting of loops. | 9 /// Determines the effective nesting of loops. |
| 120 /// | 10 /// |
| 121 /// The effective nesting of loops is different from the lexical nesting, since | 11 /// The effective nesting of loops is different from the lexical nesting, since |
| 122 /// recursive continuations can generally contain all the code following | 12 /// recursive continuations can generally contain all the code following |
| 123 /// after the loop in addition to the looping code itself. | 13 /// after the loop in addition to the looping code itself. |
| 124 /// | 14 /// |
| 125 /// For example, the 'else' branch below is not effectively part of the loop: | 15 /// For example, the 'else' branch below is not effectively part of the loop: |
| 126 /// | 16 /// |
| 127 /// let rec kont x = | 17 /// let rec kont x = |
| (...skipping 22 matching lines...) Expand all Loading... |
| 150 /// Parent pointers must be computed for [node]. | 40 /// Parent pointers must be computed for [node]. |
| 151 LoopHierarchy(FunctionDefinition node) { | 41 LoopHierarchy(FunctionDefinition node) { |
| 152 _processBlock(node.body, null); | 42 _processBlock(node.body, null); |
| 153 } | 43 } |
| 154 | 44 |
| 155 /// Returns the innermost loop which [cont] is effectively part of. | 45 /// Returns the innermost loop which [cont] is effectively part of. |
| 156 Continuation getLoopHeader(Continuation cont) { | 46 Continuation getLoopHeader(Continuation cont) { |
| 157 return cont.isRecursive ? cont : loopTarget[cont]; | 47 return cont.isRecursive ? cont : loopTarget[cont]; |
| 158 } | 48 } |
| 159 | 49 |
| 50 /// Returns the innermost loop which the given loop is part of, other |
| 51 /// than itself. |
| 52 Continuation getEnclosingLoop(Continuation loop) { |
| 53 return loopTarget[loop]; |
| 54 } |
| 55 |
| 160 /// Marks the innermost loop as a subloop of the other loop. | 56 /// Marks the innermost loop as a subloop of the other loop. |
| 161 /// | 57 /// |
| 162 /// Returns the innermost loop. | 58 /// Returns the innermost loop. |
| 163 /// | 59 /// |
| 164 /// Both continuations, [c1] and [c2] may be null (i.e. no loop). | 60 /// Both continuations, [c1] and [c2] may be null (i.e. no loop). |
| 165 /// | 61 /// |
| 166 /// A loop is said to be a subloop of an enclosing loop if it can invoke | 62 /// A loop is said to be a subloop of an enclosing loop if it can invoke |
| 167 /// that loop recursively. This information is stored in [loopTarget]. | 63 /// that loop recursively. This information is stored in [loopTarget]. |
| 168 /// | 64 /// |
| 169 /// This method is only invoked with two distinct loops if there is a | 65 /// This method is only invoked with two distinct loops if there is a |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 target = _markInnerLoop(target, catchLoop); | 144 target = _markInnerLoop(target, catchLoop); |
| 249 for (Continuation cont in callContinuations) { | 145 for (Continuation cont in callContinuations) { |
| 250 // Store the loop target on each call continuation in the basic block. | 146 // Store the loop target on each call continuation in the basic block. |
| 251 // Because we walk over call continuations as part of the basic block | 147 // Because we walk over call continuations as part of the basic block |
| 252 // traversal, these do not get their loop target set otherwise. | 148 // traversal, these do not get their loop target set otherwise. |
| 253 loopTarget[cont] = target; | 149 loopTarget[cont] = target; |
| 254 } | 150 } |
| 255 return target; | 151 return target; |
| 256 } | 152 } |
| 257 } | 153 } |
| OLD | NEW |