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