Chromium Code Reviews| 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.share_interceptors; | 5 library dart2js.cps_ir.share_interceptors; |
| 6 | 6 |
| 7 import 'optimizers.dart'; | |
| 7 import 'cps_ir_nodes.dart'; | 8 import 'cps_ir_nodes.dart'; |
| 8 import 'optimizers.dart'; | 9 import 'loop_hierarchy.dart'; |
| 9 import 'type_mask_system.dart'; | |
| 10 import '../elements/elements.dart'; | |
| 11 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 12 | 11 |
| 13 /// Merges calls to `getInterceptor` when one call is in scope of the other. | 12 /// Removes redundant `getInterceptor` calls. |
| 14 /// | |
| 15 /// Also replaces `getInterceptor` calls with an interceptor constant when | |
| 16 /// the result is known statically, and there is no interceptor already in | |
| 17 /// scope. | |
| 18 /// | 13 /// |
| 19 /// Should run after [LoopInvariantCodeMotion] so interceptors lifted out from | 14 /// The pass performs three optimizations for interceptors: |
| 20 /// loops can be merged. | 15 ///- pull interceptors out of loops |
| 16 ///- replace interceptors with constants | |
| 17 ///- share interceptors when one is in scope of the other | |
| 21 class ShareInterceptors extends RecursiveVisitor implements Pass { | 18 class ShareInterceptors extends RecursiveVisitor implements Pass { |
| 22 String get passName => 'Share interceptors'; | 19 String get passName => 'Share interceptors'; |
| 23 | 20 |
| 21 /// The innermost loop containing a given primitive. | |
| 22 final Map<Primitive, Continuation> loopHeaderFor = | |
| 23 <Primitive, Continuation>{}; | |
| 24 | |
| 25 /// An interceptor currently in scope for a given primitive. | |
| 24 final Map<Primitive, Primitive> interceptorFor = | 26 final Map<Primitive, Primitive> interceptorFor = |
| 25 <Primitive, Primitive>{}; | 27 <Primitive, Primitive>{}; |
| 26 | 28 |
| 29 /// A primitive currently in scope holding a given interceptor constant. | |
| 27 final Map<ConstantValue, Primitive> sharedConstantFor = | 30 final Map<ConstantValue, Primitive> sharedConstantFor = |
| 28 <ConstantValue, Primitive>{}; | 31 <ConstantValue, Primitive>{}; |
| 29 | 32 |
| 33 /// Interceptors to be hoisted out of the given loop. | |
| 34 final Map<Continuation, List<Primitive>> loopHoistedInterceptors = | |
| 35 <Continuation, List<Primitive>>{}; | |
| 36 | |
| 37 LoopHierarchy loopHierarchy; | |
| 38 Continuation currentLoopHeader; | |
| 39 | |
| 30 void rewrite(FunctionDefinition node) { | 40 void rewrite(FunctionDefinition node) { |
| 41 loopHierarchy = new LoopHierarchy(node); | |
| 31 visit(node.body); | 42 visit(node.body); |
| 32 } | 43 } |
| 33 | 44 |
| 34 @override | 45 @override |
| 46 Expression traverseContinuation(Continuation cont) { | |
| 47 Continuation oldLoopHeader = currentLoopHeader; | |
| 48 pushAction(() { | |
| 49 currentLoopHeader = oldLoopHeader; | |
| 50 }); | |
| 51 currentLoopHeader = loopHierarchy.getLoopHeader(cont); | |
| 52 for (Parameter param in cont.parameters) { | |
| 53 loopHeaderFor[param] = currentLoopHeader; | |
| 54 } | |
| 55 if (cont.isRecursive) { | |
| 56 pushAction(() { | |
| 57 // After the loop body has been processed, all interceptors hoisted | |
| 58 // to this loop fall out of scope and should be removed from the | |
| 59 // environment. | |
| 60 List<Primitive> hoisted = loopHoistedInterceptors[cont]; | |
| 61 if (hoisted != null) { | |
| 62 for (Primitive interceptor in hoisted) { | |
| 63 if (interceptor is Interceptor) { | |
| 64 Primitive input = interceptor.input.definition; | |
| 65 assert(interceptorFor[input] == interceptor); | |
| 66 interceptorFor.remove(input); | |
| 67 } else if (interceptor is Constant) { | |
| 68 assert(sharedConstantFor[interceptor.value] == interceptor); | |
| 69 sharedConstantFor.remove(interceptor.value); | |
| 70 } else { | |
| 71 throw "Unexpected interceptor: $interceptor"; | |
| 72 } | |
| 73 } | |
| 74 } | |
| 75 }); | |
| 76 } | |
| 77 return cont.body; | |
| 78 } | |
| 79 | |
| 80 @override | |
| 35 Expression traverseLetPrim(LetPrim node) { | 81 Expression traverseLetPrim(LetPrim node) { |
| 36 if (node.primitive is Interceptor) { | 82 loopHeaderFor[node.primitive] = currentLoopHeader; |
| 37 Interceptor interceptor = node.primitive; | 83 Expression next = node.body; |
| 38 Primitive input = interceptor.input.definition; | 84 if (node.primitive is! Interceptor) { |
| 39 Primitive existing = interceptorFor[input]; | 85 return next; |
| 86 } | |
| 87 Interceptor interceptor = node.primitive; | |
| 88 Primitive input = interceptor.input.definition; | |
| 89 | |
| 90 // Try to reuse an existing interceptor for the same input. | |
| 91 Primitive existing = interceptorFor[input]; | |
| 92 if (existing != null) { | |
| 93 if (existing is Interceptor) { | |
| 94 existing.interceptedClasses.addAll(interceptor.interceptedClasses); | |
| 95 } | |
| 96 existing.substituteFor(interceptor); | |
| 97 InteriorNode parent = node.parent; | |
| 98 parent.body = node.body; | |
| 99 node.body.parent = parent; | |
| 100 interceptor.input.unlink(); | |
|
sra1
2015/09/25 18:26:17
Perhaps we should add a more general dead node rem
asgerf
2015/09/28 10:50:14
I was reluctant to do this, but I admit it does ma
| |
| 101 return next; | |
| 102 } | |
| 103 | |
| 104 // There is no interceptor obtained from this particular input, but | |
| 105 // there might one obtained from another input that is known to | |
| 106 // have the same result, so try to reuse that. | |
| 107 InterceptorConstantValue constant = interceptor.constantValue; | |
| 108 if (constant != null) { | |
| 109 existing = sharedConstantFor[constant]; | |
| 40 if (existing != null) { | 110 if (existing != null) { |
| 41 if (existing is Interceptor) { | |
| 42 existing.interceptedClasses.addAll(interceptor.interceptedClasses); | |
| 43 } | |
| 44 existing.substituteFor(interceptor); | 111 existing.substituteFor(interceptor); |
| 45 } else if (interceptor.constantValue != null) { | 112 InteriorNode parent = node.parent; |
| 46 InterceptorConstantValue value = interceptor.constantValue; | 113 parent.body = node.body; |
| 47 // There is no interceptor obtained from this particular input, but | 114 node.body.parent = parent; |
| 48 // there might one obtained from another input that is known to | 115 interceptor.input.unlink(); |
|
sra1
2015/09/25 18:26:17
These five lines are the same as above.
If we don'
asgerf
2015/09/28 10:50:14
(see above)
| |
| 49 // have the same result, so try to reuse that. | 116 return next; |
| 50 Primitive shared = sharedConstantFor[value]; | 117 } |
| 51 if (shared != null) { | 118 |
| 52 shared.substituteFor(interceptor); | 119 // The interceptor could not be shared. Replace it with a constant. |
| 53 } else { | 120 Constant constantPrim = new Constant(constant); |
| 54 Constant constant = new Constant(value); | 121 node.primitive = constantPrim; |
| 55 constant.hint = interceptor.hint; | 122 constantPrim.hint = interceptor.hint; |
| 56 node.primitive = constant; | 123 constantPrim.type = interceptor.type; |
| 57 constant.parent = node; | 124 constantPrim.substituteFor(interceptor); |
| 58 interceptor.input.unlink(); | 125 interceptor.input.unlink(); |
| 59 constant.substituteFor(interceptor); | 126 sharedConstantFor[constant] = constantPrim; |
| 60 interceptorFor[input] = constant; | 127 } else { |
| 61 sharedConstantFor[value] = constant; | 128 interceptorFor[input] = interceptor; |
| 62 pushAction(() { | 129 } |
| 63 interceptorFor.remove(input); | 130 |
| 64 sharedConstantFor.remove(value); | 131 // Determine the outermost loop where the input to the interceptor call |
| 65 | 132 // is available. Constant interceptors take no input and can thus be |
| 66 if (constant.hasExactlyOneUse) { | 133 // hoisted all way to the top-level. |
| 67 // As a heuristic, always sink single-use interceptor constants | 134 Continuation referencedLoop = constant != null |
| 68 // to their use, even if it is inside a loop. | 135 ? null |
| 69 Expression use = getEnclosingExpression(constant.firstRef.parent); | 136 : lowestCommonAncestor(loopHeaderFor[input], currentLoopHeader); |
| 70 InteriorNode parent = node.parent; | 137 if (referencedLoop != currentLoopHeader) { |
| 71 parent.body = node.body; | 138 // [referencedLoop] contains the binding for [input], so we cannot hoist |
| 72 node.body.parent = parent; | 139 // the interceptor outside that loop. Find the loop nested one level |
| 73 | 140 // inside referencedLoop, and hoist the interceptor just outside that one. |
| 74 InteriorNode useParent = use.parent; | 141 Continuation loop = currentLoopHeader; |
| 75 useParent.body = node; | 142 Continuation enclosing = loopHierarchy.getEnclosingLoop(loop); |
| 76 node.body = use; | 143 while (enclosing != referencedLoop) { |
| 77 use.parent = node; | 144 assert(loop != null); |
| 78 node.parent = useParent; | 145 loop = enclosing; |
| 79 } | 146 enclosing = loopHierarchy.getEnclosingLoop(loop); |
| 80 }); | 147 } |
| 81 } | 148 assert(loop != null); |
| 149 | |
| 150 // Remove LetPrim from its current position. | |
| 151 InteriorNode parent = node.parent; | |
| 152 parent.body = node.body; | |
| 153 node.body.parent = parent; | |
| 154 | |
| 155 // Insert the LetPrim immediately before the loop. | |
| 156 LetCont loopBinding = loop.parent; | |
| 157 InteriorNode newParent = loopBinding.parent; | |
| 158 newParent.body = node; | |
| 159 node.body = loopBinding; | |
| 160 loopBinding.parent = node; | |
| 161 node.parent = newParent; | |
| 162 | |
| 163 // A different loop now contains the interceptor. | |
| 164 loopHeaderFor[node.primitive] = enclosing; | |
| 165 | |
| 166 // Register the interceptor as hoisted to that loop, so it will be | |
| 167 // removed from the environment when it falls out of scope. | |
| 168 loopHoistedInterceptors | |
| 169 .putIfAbsent(loop, () => <Primitive>[]) | |
| 170 .add(node.primitive); | |
| 171 } else if (constant != null) { | |
| 172 // The LetPrim was not hoisted. Remove the bound interceptor from the | |
| 173 // environment when leaving the LetPrim body. | |
| 174 pushAction(() { | |
| 175 assert(sharedConstantFor[constant] == node.primitive); | |
| 176 sharedConstantFor.remove(constant); | |
| 177 }); | |
| 178 } else { | |
| 179 pushAction(() { | |
| 180 assert(interceptorFor[input] == node.primitive); | |
| 181 interceptorFor.remove(input); | |
| 182 }); | |
| 183 } | |
| 184 return next; | |
| 185 } | |
| 186 | |
| 187 /// Returns the the innermost loop that effectively encloses both | |
| 188 /// c1 and c2 (or `null` if there is no such loop). | |
| 189 Continuation lowestCommonAncestor(Continuation c1, Continuation c2) { | |
| 190 int d1 = getDepth(c1), d2 = getDepth(c2); | |
| 191 while (c1 != c2) { | |
| 192 if (d1 <= d2) { | |
| 193 c2 = loopHierarchy.getEnclosingLoop(c2); | |
| 194 d2 = getDepth(c2); | |
| 82 } else { | 195 } else { |
| 83 interceptorFor[input] = interceptor; | 196 c1 = loopHierarchy.getEnclosingLoop(c1); |
| 84 pushAction(() { | 197 d1 = getDepth(c1); |
| 85 interceptorFor.remove(input); | 198 } |
| 86 }); | 199 } |
| 87 } | 200 return c1; |
| 88 } | 201 } |
| 89 return node.body; | 202 |
| 90 } | 203 int getDepth(Continuation loop) { |
| 91 | 204 if (loop == null) return -1; |
| 92 Expression getEnclosingExpression(Node node) { | 205 return loopHierarchy.loopDepth[loop]; |
| 93 while (node is! Expression) { | |
| 94 node = node.parent; | |
| 95 } | |
| 96 return node; | |
| 97 } | 206 } |
| 98 } | 207 } |
| OLD | NEW |