| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 dart2js.cps_ir.optimizers; | 5 part of dart2js.cps_ir.optimizers; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described | 8 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described |
| 9 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. | 9 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. |
| 10 */ | 10 */ |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 void _removeNode(InteriorNode node) { | 39 void _removeNode(InteriorNode node) { |
| 40 Node body = node.body; | 40 Node body = node.body; |
| 41 InteriorNode parent = node.parent; | 41 InteriorNode parent = node.parent; |
| 42 assert(parent.body == node); | 42 assert(parent.body == node); |
| 43 | 43 |
| 44 body.parent = parent; | 44 body.parent = parent; |
| 45 parent.body = body; | 45 parent.body = body; |
| 46 node.parent = _DELETED; | 46 node.parent = _DELETED; |
| 47 } | 47 } |
| 48 | 48 |
| 49 /// Remove a given continuation from the CPS graph. The LetCont itself is | |
| 50 /// removed if the given continuation is the only binding. | |
| 51 void _removeContinuation(Continuation cont) { | |
| 52 LetCont parent = cont.parent; | |
| 53 if (parent.continuations.length == 1) { | |
| 54 assert(cont.parent_index == 0); | |
| 55 _removeNode(parent); | |
| 56 } else { | |
| 57 List<Continuation> continuations = parent.continuations; | |
| 58 for (int i = cont.parent_index; i < continuations.length - 1; ++i) { | |
| 59 Continuation current = continuations[i + 1]; | |
| 60 continuations[i] = current; | |
| 61 current.parent_index = i; | |
| 62 } | |
| 63 continuations.removeLast(); | |
| 64 } | |
| 65 cont.parent = _DELETED; | |
| 66 } | |
| 67 | |
| 68 void _processTask(_ReductionTask task) { | 49 void _processTask(_ReductionTask task) { |
| 69 // Skip tasks for deleted nodes. | 50 // Lazily skip tasks for deleted nodes. |
| 70 if (task.node.parent == _DELETED) { | 51 if (task.node.parent == _DELETED) { |
| 71 return; | 52 return; |
| 72 } | 53 } |
| 73 | 54 |
| 74 switch (task.kind) { | 55 switch (task.kind) { |
| 75 case _ReductionKind.DEAD_VAL: | 56 case _ReductionKind.DEAD_VAL: |
| 76 _reduceDeadVal(task); | 57 _reduceDeadVal(task); |
| 77 break; | 58 break; |
| 78 case _ReductionKind.DEAD_CONT: | 59 case _ReductionKind.DEAD_CONT: |
| 79 _reduceDeadCont(task); | 60 _reduceDeadCont(task); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 101 // Perform bookkeeping on removed body and scan for new redexes. | 82 // Perform bookkeeping on removed body and scan for new redexes. |
| 102 new _RemovalRedexVisitor(_worklist).visit(letPrim.primitive); | 83 new _RemovalRedexVisitor(_worklist).visit(letPrim.primitive); |
| 103 } | 84 } |
| 104 | 85 |
| 105 /// Applies the dead-cont reduction: | 86 /// Applies the dead-cont reduction: |
| 106 /// letcont k x = E0 in E1 -> E1 (k not free in E1). | 87 /// letcont k x = E0 in E1 -> E1 (k not free in E1). |
| 107 void _reduceDeadCont(_ReductionTask task) { | 88 void _reduceDeadCont(_ReductionTask task) { |
| 108 assert(_isDeadCont(task.node)); | 89 assert(_isDeadCont(task.node)); |
| 109 | 90 |
| 110 // Remove dead continuation. | 91 // Remove dead continuation. |
| 111 Continuation cont = task.node; | 92 LetCont letCont = task.node; |
| 112 _removeContinuation(cont); | 93 _removeNode(letCont); |
| 113 | 94 |
| 114 // Perform bookkeeping on removed body and scan for new redexes. | 95 // Perform bookkeeping on removed body and scan for new redexes. |
| 115 new _RemovalRedexVisitor(_worklist).visit(cont); | 96 new _RemovalRedexVisitor(_worklist).visit(letCont.continuation); |
| 116 } | 97 } |
| 117 | 98 |
| 118 /// Applies the beta-cont-lin reduction: | 99 /// Applies the beta-cont-lin reduction: |
| 119 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). | 100 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). |
| 120 void _reduceBetaContLin(_ReductionTask task) { | 101 void _reduceBetaContLin(_ReductionTask task) { |
| 121 // Might have been mutated, recheck if reduction is still valid. | 102 // Might have been mutated, recheck if reduction is still valid. |
| 122 // In the following example, the beta-cont-lin reduction of k0 could have | 103 // In the following example, the beta-cont-lin reduction of k0 could have |
| 123 // been invalidated by removal of the dead continuation k1: | 104 // been invalidated by removal of the dead continuation k1: |
| 124 // | 105 // |
| 125 // letcont k0 x0 = E0 in | 106 // letcont k0 x0 = E0 in |
| 126 // letcont k1 x1 = k0 x1 in | 107 // letcont k1 x1 = k0 x1 in |
| 127 // return x2 | 108 // return x2 |
| 128 if (!_isBetaContLin(task.node)) { | 109 if (!_isBetaContLin(task.node)) { |
| 129 return; | 110 return; |
| 130 } | 111 } |
| 131 | 112 |
| 132 // Remove the continuation. | 113 // Remove the continuation. |
| 133 Continuation cont = task.node; | 114 LetCont letCont = task.node; |
| 134 _removeContinuation(cont); | 115 Continuation cont = letCont.continuation; |
| 116 _removeNode(letCont); |
| 135 | 117 |
| 136 // Replace its invocation with the continuation body. | 118 // Replace its invocation with the continuation body. |
| 137 InvokeContinuation invoke = cont.firstRef.parent; | 119 InvokeContinuation invoke = cont.firstRef.parent; |
| 138 InteriorNode invokeParent = invoke.parent; | 120 InteriorNode invokeParent = invoke.parent; |
| 139 | 121 |
| 140 cont.body.parent = invokeParent; | 122 cont.body.parent = invokeParent; |
| 141 invokeParent.body = cont.body; | 123 invokeParent.body = cont.body; |
| 142 | 124 |
| 143 // Substitute the invocation argument for the continuation parameter. | 125 // Substitute the invocation argument for the continuation parameter. |
| 144 for (int i = 0; i < invoke.arguments.length; i++) { | 126 for (int i = 0; i < invoke.arguments.length; i++) { |
| 145 Reference argRef = invoke.arguments[i]; | 127 Reference argRef = invoke.arguments[i]; |
| 146 argRef.definition.substituteFor(cont.parameters[i]); | 128 argRef.definition.substituteFor(cont.parameters[i]); |
| 147 } | 129 } |
| 148 | 130 |
| 149 // Perform bookkeeping on substituted body and scan for new redexes. | 131 // Perform bookkeeping on removed body and scan for new redexes. |
| 150 new _RemovalRedexVisitor(_worklist).visit(invoke); | 132 new _RemovalRedexVisitor(_worklist).visit(invoke); |
| 151 } | 133 } |
| 152 | 134 |
| 153 /// Applies the eta-cont reduction: | 135 /// Applies the eta-cont reduction: |
| 154 /// letcont k x = j x in E -> E[j/k]. | 136 /// letcont k x = j x in E -> E[j/k]. |
| 155 /// If k is unused, degenerates to dead-cont. | 137 /// If k is unused, degenerates to dead-cont. |
| 156 void _reduceEtaCont(_ReductionTask task) { | 138 void _reduceEtaCont(_ReductionTask task) { |
| 157 // Might have been mutated, recheck if reduction is still valid. | 139 // Might have been mutated, recheck if reduction is still valid. |
| 158 // In the following example, the eta-cont reduction of k1 could have been | 140 // In the following example, the eta-cont reduction of k1 could have been |
| 159 // invalidated by an earlier beta-cont-lin reduction of k0. | 141 // invalidated by an earlier beta-cont-lin reduction of k0. |
| 160 // | 142 // |
| 161 // letcont k0 x0 = E0 in | 143 // letcont k0 x0 = E0 in |
| 162 // letcont k1 x1 = k0 x1 in E1 | 144 // letcont k1 x1 = k0 x1 in E1 |
| 163 if (!_isEtaCont(task.node)) { | 145 if (!_isEtaCont(task.node)) { |
| 164 return; | 146 return; |
| 165 } | 147 } |
| 166 | 148 |
| 167 // Remove the continuation. | 149 // Remove the continuation. |
| 168 Continuation cont = task.node; | 150 LetCont letCont = task.node; |
| 169 _removeContinuation(cont); | 151 Continuation cont = letCont.continuation; |
| 152 _removeNode(letCont); |
| 170 | 153 |
| 171 InvokeContinuation invoke = cont.body; | 154 InvokeContinuation invoke = cont.body; |
| 172 Continuation wrappedCont = invoke.continuation.definition; | 155 Continuation wrappedCont = invoke.continuation.definition; |
| 173 | 156 |
| 174 // Replace all occurrences with the wrapped continuation. | 157 // Replace all occurrences with the wrapped continuation. |
| 175 wrappedCont.substituteFor(cont); | 158 wrappedCont.substituteFor(cont); |
| 176 | 159 |
| 177 // Perform bookkeeping on removed body and scan for new redexes. | 160 // Perform bookkeeping on removed body and scan for new redexes. |
| 178 new _RemovalRedexVisitor(_worklist).visit(cont); | 161 new _RemovalRedexVisitor(_worklist).visit(cont); |
| 179 } | 162 } |
| 180 } | 163 } |
| 181 | 164 |
| 182 /// Returns true iff the bound primitive is unused. | 165 /// Returns true iff the bound primitive is unused. |
| 183 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; | 166 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; |
| 184 | 167 |
| 185 /// Returns true iff the continuation is unused. | 168 /// Returns true iff the bound continuation is unused. |
| 186 bool _isDeadCont(Continuation cont) { | 169 bool _isDeadCont(LetCont node) => !node.continuation.hasAtLeastOneUse; |
| 187 return !cont.hasAtLeastOneUse && !cont.isReturnContinuation; | |
| 188 } | |
| 189 | 170 |
| 190 /// Returns true iff the continuation is used exactly once, and that | 171 /// Returns true iff the bound continuation is used exactly once, and that |
| 191 /// use is as the continuation of a continuation invocation. | 172 /// use is as the receiver of a continuation invocation. |
| 192 bool _isBetaContLin(Continuation cont) { | 173 bool _isBetaContLin(LetCont node) { |
| 174 Continuation cont = node.continuation; |
| 193 if (!cont.hasExactlyOneUse) { | 175 if (!cont.hasExactlyOneUse) { |
| 194 return false; | 176 return false; |
| 195 } | 177 } |
| 196 | 178 |
| 197 if (cont.firstRef.parent is InvokeContinuation) { | 179 if (cont.firstRef.parent is InvokeContinuation) { |
| 198 InvokeContinuation invoke = cont.firstRef.parent; | 180 InvokeContinuation invoke = cont.firstRef.parent; |
| 199 return (cont == invoke.continuation.definition); | 181 return (cont == invoke.continuation.definition); |
| 200 } | 182 } |
| 201 | 183 |
| 202 return false; | 184 return false; |
| 185 |
| 203 } | 186 } |
| 204 | 187 |
| 205 /// Returns true iff the continuation consists of a continuation | 188 /// Returns true iff the bound continuation consists of a continuation |
| 206 /// invocation, passing on all parameters. Special cases exist (see below). | 189 /// invocation, passing on all parameters. Special cases exist (see below). |
| 207 bool _isEtaCont(Continuation cont) { | 190 bool _isEtaCont(LetCont node) { |
| 208 if (cont.body is! InvokeContinuation) { | 191 Continuation cont = node.continuation; |
| 192 if (!(cont.body is InvokeContinuation)) { |
| 209 return false; | 193 return false; |
| 210 } | 194 } |
| 211 | 195 |
| 212 InvokeContinuation invoke = cont.body; | 196 InvokeContinuation invoke = cont.body; |
| 213 Continuation invokedCont = invoke.continuation.definition; | 197 Continuation invokedCont = invoke.continuation.definition; |
| 214 | 198 |
| 215 // Do not eta-reduce return join-points since the resulting code is worse | 199 // Do not eta-reduce return join-points since the resulting code is worse |
| 216 // in the common case (i.e. returns are moved inside `if` branches). | 200 // in the common case (i.e. returns are moved inside `if` branches). |
| 217 if (invokedCont.isReturnContinuation) { | 201 if (invokedCont.isReturnContinuation) { |
| 218 return false; | 202 return false; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 if (node.parent == ShrinkingReducer._DELETED) { | 235 if (node.parent == ShrinkingReducer._DELETED) { |
| 252 return; | 236 return; |
| 253 } else if (_isDeadVal(node)) { | 237 } else if (_isDeadVal(node)) { |
| 254 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); | 238 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); |
| 255 } | 239 } |
| 256 } | 240 } |
| 257 | 241 |
| 258 void processLetCont(LetCont node) { | 242 void processLetCont(LetCont node) { |
| 259 if (node.parent == ShrinkingReducer._DELETED) { | 243 if (node.parent == ShrinkingReducer._DELETED) { |
| 260 return; | 244 return; |
| 261 } | 245 } else if (_isDeadCont(node)) { |
| 262 for (int i = 0; i < node.continuations.length; ++i) { | 246 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
| 263 Continuation cont = node.continuations[i]; | 247 } else if (_isEtaCont(node)) { |
| 264 if (_isDeadCont(cont)) { | 248 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
| 265 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); | 249 } else if (_isBetaContLin(node)){ |
| 266 } else if (_isEtaCont(cont)) { | 250 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); |
| 267 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, cont)); | |
| 268 } else if (_isBetaContLin(cont)){ | |
| 269 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); | |
| 270 } | |
| 271 } | 251 } |
| 272 } | 252 } |
| 273 } | 253 } |
| 274 | 254 |
| 275 /// Traverses a deleted CPS term, marking existing tasks associated with a node | 255 /// Traverses a deleted CPS term, marking existing tasks associated with a node |
| 276 /// within the term as deleted (which causes them to be skipped lazily when | 256 /// within the term as deleted (which causes them to be skipped lazily when |
| 277 /// popped from the worklist), and adding newly created redexes to the worklist. | 257 /// popped from the worklist), and adding newly created redexes to the worklist. |
| 278 class _RemovalRedexVisitor extends _RedexVisitor { | 258 class _RemovalRedexVisitor extends _RedexVisitor { |
| 279 _RemovalRedexVisitor(Set<_ReductionTask> worklist) : super(worklist); | 259 _RemovalRedexVisitor(Set<_ReductionTask> worklist) : super(worklist); |
| 280 | 260 |
| 281 void processLetPrim(LetPrim node) { | 261 void processLetPrim(LetPrim node) { |
| 282 node.parent = ShrinkingReducer._DELETED; | 262 node.parent = ShrinkingReducer._DELETED; |
| 283 } | 263 } |
| 284 | 264 |
| 285 void processLetCont(LetCont node) { | 265 void processLetCont(LetCont node) { |
| 286 node.parent = ShrinkingReducer._DELETED; | 266 node.parent = ShrinkingReducer._DELETED; |
| 287 } | 267 } |
| 288 | 268 |
| 289 void processContinuation(Continuation node) { | |
| 290 node.parent = ShrinkingReducer._DELETED; | |
| 291 } | |
| 292 | |
| 293 void processReference(Reference reference) { | 269 void processReference(Reference reference) { |
| 294 reference.unlink(); | 270 reference.unlink(); |
| 295 | 271 |
| 296 if (reference.definition is Primitive) { | 272 if (reference.definition is Primitive) { |
| 297 Primitive primitive = reference.definition; | 273 Primitive primitive = reference.definition; |
| 298 Node parent = primitive.parent; | 274 Node parent = primitive.parent; |
| 299 if (parent is LetPrim && _isDeadVal(parent)) { | 275 if (parent is LetPrim && _isDeadVal(parent)) { |
| 300 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 276 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 301 } | 277 } |
| 302 } else if (reference.definition is Continuation) { | 278 } else if (reference.definition is Continuation) { |
| 303 Continuation cont = reference.definition; | 279 Continuation cont = reference.definition; |
| 304 if (cont.isRecursive && cont.hasAtMostOneUse) { | 280 if (cont.isRecursive && cont.hasAtMostOneUse) { |
| 305 // Convert recursive to nonrecursive continuations. | 281 // Convert recursive to nonrecursive continuations. |
| 306 // If the continuation is still in use, it is either dead and will be | 282 // If the continuation is still in use, it is either dead and will be |
| 307 // removed, or it is called nonrecursively outside its body. | 283 // removed, or it is called nonrecursively outside its body. |
| 308 cont.isRecursive = false; | 284 cont.isRecursive = false; |
| 309 } | 285 } |
| 310 if (_isDeadCont(cont)) { | 286 Node parent = cont.parent; |
| 311 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); | 287 if (parent is LetCont && _isDeadCont(parent)) { |
| 288 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, parent)); |
| 312 } | 289 } |
| 313 } | 290 } |
| 314 } | 291 } |
| 315 } | 292 } |
| 316 | 293 |
| 317 /// Traverses the CPS term and sets node.parent for each visited node. | 294 /// Traverses the CPS term and sets node.parent for each visited node. |
| 318 class ParentVisitor extends RecursiveVisitor { | 295 class ParentVisitor extends RecursiveVisitor { |
| 296 |
| 319 processFunctionDefinition(FunctionDefinition node) { | 297 processFunctionDefinition(FunctionDefinition node) { |
| 320 node.body.parent = node; | 298 node.body.parent = node; |
| 321 node.parameters.forEach((Definition p) => p.parent = node); | 299 node.parameters.forEach((Definition p) => p.parent = node); |
| 322 } | 300 } |
| 323 | 301 |
| 324 processRunnableBody(RunnableBody node) { | 302 processRunnableBody(RunnableBody node) { |
| 325 node.body.parent = node; | 303 node.body.parent = node; |
| 326 } | 304 } |
| 327 | 305 |
| 328 processConstructorDefinition(ConstructorDefinition node) { | 306 processConstructorDefinition(ConstructorDefinition node) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 341 node.arguments.forEach( | 319 node.arguments.forEach( |
| 342 (RunnableBody argument) => argument.body.parent = node); | 320 (RunnableBody argument) => argument.body.parent = node); |
| 343 } | 321 } |
| 344 | 322 |
| 345 processLetPrim(LetPrim node) { | 323 processLetPrim(LetPrim node) { |
| 346 node.primitive.parent = node; | 324 node.primitive.parent = node; |
| 347 node.body.parent = node; | 325 node.body.parent = node; |
| 348 } | 326 } |
| 349 | 327 |
| 350 processLetCont(LetCont node) { | 328 processLetCont(LetCont node) { |
| 351 for (int i = 0; i < node.continuations.length; ++i) { | 329 node.continuation.parent = node; |
| 352 Continuation cont = node.continuations[i]; | |
| 353 cont.parent = node; | |
| 354 cont.parent_index = i; | |
| 355 } | |
| 356 node.body.parent = node; | 330 node.body.parent = node; |
| 357 } | 331 } |
| 358 | 332 |
| 359 processInvokeStatic(InvokeStatic node) { | 333 processInvokeStatic(InvokeStatic node) { |
| 334 node.continuation.parent = node; |
| 360 node.arguments.forEach((Reference ref) => ref.parent = node); | 335 node.arguments.forEach((Reference ref) => ref.parent = node); |
| 361 node.continuation.parent = node; | |
| 362 } | 336 } |
| 363 | 337 |
| 364 processInvokeContinuation(InvokeContinuation node) { | 338 processInvokeContinuation(InvokeContinuation node) { |
| 365 node.continuation.parent = node; | 339 node.continuation.parent = node; |
| 366 node.arguments.forEach((Reference ref) => ref.parent = node); | 340 node.arguments.forEach((Reference ref) => ref.parent = node); |
| 367 } | 341 } |
| 368 | 342 |
| 369 processInvokeMethod(InvokeMethod node) { | 343 processInvokeMethod(InvokeMethod node) { |
| 370 node.receiver.parent = node; | 344 node.receiver.parent = node; |
| 371 node.continuation.parent = node; | 345 node.continuation.parent = node; |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 502 } | 476 } |
| 503 | 477 |
| 504 String toString() => "$kind: $node"; | 478 String toString() => "$kind: $node"; |
| 505 } | 479 } |
| 506 | 480 |
| 507 /// A dummy class used solely to mark nodes as deleted once they are removed | 481 /// A dummy class used solely to mark nodes as deleted once they are removed |
| 508 /// from a term. | 482 /// from a term. |
| 509 class _DeletedNode extends Node { | 483 class _DeletedNode extends Node { |
| 510 accept(_) => null; | 484 accept(_) => null; |
| 511 } | 485 } |
| OLD | NEW |