| 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 */ |
| 11 class ShrinkingReducer extends PassMixin { | 11 class ShrinkingReducer extends PassMixin { |
| 12 _RedexVisitor _redexVisitor; | |
| 13 Set<_ReductionTask> _worklist; | 12 Set<_ReductionTask> _worklist; |
| 14 | 13 |
| 15 static final _DeletedNode _DELETED = new _DeletedNode(); | 14 static final _DeletedNode _DELETED = new _DeletedNode(); |
| 16 | 15 |
| 17 /// Applies shrinking reductions to root, mutating root in the process. | 16 /// Applies shrinking reductions to root, mutating root in the process. |
| 18 @override | 17 @override |
| 19 void rewriteExecutableDefinition(ExecutableDefinition root) { | 18 void rewriteExecutableDefinition(ExecutableDefinition root) { |
| 20 _worklist = new Set<_ReductionTask>(); | 19 _worklist = new Set<_ReductionTask>(); |
| 21 _redexVisitor = new _RedexVisitor(_worklist); | 20 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist); |
| 22 | 21 |
| 23 // Set all parent pointers. | 22 // Set all parent pointers. |
| 24 new ParentVisitor().visit(root); | 23 new ParentVisitor().visit(root); |
| 25 | 24 |
| 26 // Sweep over the term, collecting redexes into the worklist. | 25 // Sweep over the term, collecting redexes into the worklist. |
| 27 _redexVisitor.visit(root); | 26 redexVisitor.visit(root); |
| 28 | 27 |
| 29 // Process the worklist. | 28 // Process the worklist. |
| 30 while (_worklist.isNotEmpty) { | 29 while (_worklist.isNotEmpty) { |
| 31 _ReductionTask task = _worklist.first; | 30 _ReductionTask task = _worklist.first; |
| 32 _worklist.remove(task); | 31 _worklist.remove(task); |
| 33 _processTask(task); | 32 _processTask(task); |
| 34 } | 33 } |
| 35 } | 34 } |
| 36 | 35 |
| 37 /// Removes the given node from the CPS graph, replacing it with its body | 36 /// Removes the given node from the CPS graph, replacing it with its body |
| 38 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. | 37 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. |
| 39 void _removeNode(InteriorNode node) { | 38 void _removeNode(InteriorNode node) { |
| 40 Node body = node.body; | 39 Node body = node.body; |
| 41 InteriorNode parent = node.parent; | 40 InteriorNode parent = node.parent; |
| 42 assert(parent.body == node); | 41 assert(parent.body == node); |
| 43 | 42 |
| 44 body.parent = parent; | 43 body.parent = parent; |
| 45 parent.body = body; | 44 parent.body = body; |
| 46 node.parent = _DELETED; | 45 node.parent = _DELETED; |
| 47 } | 46 } |
| 48 | 47 |
| 48 /// Remove a given continuation from the CPS graph. The LetCont itself is |
| 49 /// removed if the given continuation is the only binding. |
| 50 void _removeContinuation(Continuation cont) { |
| 51 LetCont parent = cont.parent; |
| 52 if (parent.continuations.length == 1) { |
| 53 assert(cont.parent_index == 0); |
| 54 _removeNode(parent); |
| 55 } else { |
| 56 List<Continuation> continuations = parent.continuations; |
| 57 for (int i = cont.parent_index; i < continuations.length - 1; ++i) { |
| 58 Continuation current = continuations[i + 1]; |
| 59 continuations[i] = current; |
| 60 current.parent_index = i; |
| 61 } |
| 62 continuations.removeLast(); |
| 63 } |
| 64 cont.parent = _DELETED; |
| 65 } |
| 66 |
| 49 void _processTask(_ReductionTask task) { | 67 void _processTask(_ReductionTask task) { |
| 50 // Lazily skip tasks for deleted nodes. | 68 // Skip tasks for deleted nodes. |
| 51 if (task.node.parent == _DELETED) { | 69 if (task.node.parent == _DELETED) { |
| 52 return; | 70 return; |
| 53 } | 71 } |
| 54 | 72 |
| 55 switch (task.kind) { | 73 switch (task.kind) { |
| 56 case _ReductionKind.DEAD_VAL: | 74 case _ReductionKind.DEAD_VAL: |
| 57 _reduceDeadVal(task); | 75 _reduceDeadVal(task); |
| 58 break; | 76 break; |
| 59 case _ReductionKind.DEAD_CONT: | 77 case _ReductionKind.DEAD_CONT: |
| 60 _reduceDeadCont(task); | 78 _reduceDeadCont(task); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 73 /// Applies the dead-val reduction: | 91 /// Applies the dead-val reduction: |
| 74 /// letprim x = V in E -> E (x not free in E). | 92 /// letprim x = V in E -> E (x not free in E). |
| 75 void _reduceDeadVal(_ReductionTask task) { | 93 void _reduceDeadVal(_ReductionTask task) { |
| 76 assert(_isDeadVal(task.node)); | 94 assert(_isDeadVal(task.node)); |
| 77 | 95 |
| 78 // Remove dead primitive. | 96 // Remove dead primitive. |
| 79 LetPrim letPrim = task.node;; | 97 LetPrim letPrim = task.node;; |
| 80 _removeNode(letPrim); | 98 _removeNode(letPrim); |
| 81 | 99 |
| 82 // Perform bookkeeping on removed body and scan for new redexes. | 100 // Perform bookkeeping on removed body and scan for new redexes. |
| 83 new _RemovalRedexVisitor(_worklist).visit(letPrim.primitive); | 101 new _RemovalVisitor(_worklist).visit(letPrim.primitive); |
| 84 } | 102 } |
| 85 | 103 |
| 86 /// Applies the dead-cont reduction: | 104 /// Applies the dead-cont reduction: |
| 87 /// letcont k x = E0 in E1 -> E1 (k not free in E1). | 105 /// letcont k x = E0 in E1 -> E1 (k not free in E1). |
| 88 void _reduceDeadCont(_ReductionTask task) { | 106 void _reduceDeadCont(_ReductionTask task) { |
| 89 assert(_isDeadCont(task.node)); | 107 assert(_isDeadCont(task.node)); |
| 90 | 108 |
| 91 // Remove dead continuation. | 109 // Remove dead continuation. |
| 92 LetCont letCont = task.node; | 110 Continuation cont = task.node; |
| 93 _removeNode(letCont); | 111 _removeContinuation(cont); |
| 94 | 112 |
| 95 // Perform bookkeeping on removed body and scan for new redexes. | 113 // Perform bookkeeping on removed body and scan for new redexes. |
| 96 new _RemovalRedexVisitor(_worklist).visit(letCont.continuation); | 114 new _RemovalVisitor(_worklist).visit(cont); |
| 97 } | 115 } |
| 98 | 116 |
| 99 /// Applies the beta-cont-lin reduction: | 117 /// Applies the beta-cont-lin reduction: |
| 100 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). | 118 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). |
| 101 void _reduceBetaContLin(_ReductionTask task) { | 119 void _reduceBetaContLin(_ReductionTask task) { |
| 102 // Might have been mutated, recheck if reduction is still valid. | 120 // Might have been mutated, recheck if reduction is still valid. |
| 103 // In the following example, the beta-cont-lin reduction of k0 could have | 121 // In the following example, the beta-cont-lin reduction of k0 could have |
| 104 // been invalidated by removal of the dead continuation k1: | 122 // been invalidated by removal of the dead continuation k1: |
| 105 // | 123 // |
| 106 // letcont k0 x0 = E0 in | 124 // letcont k0 x0 = E0 in |
| 107 // letcont k1 x1 = k0 x1 in | 125 // letcont k1 x1 = k0 x1 in |
| 108 // return x2 | 126 // return x2 |
| 109 if (!_isBetaContLin(task.node)) { | 127 if (!_isBetaContLin(task.node)) { |
| 110 return; | 128 return; |
| 111 } | 129 } |
| 112 | 130 |
| 113 // Remove the continuation. | 131 // Remove the continuation. |
| 114 LetCont letCont = task.node; | 132 Continuation cont = task.node; |
| 115 Continuation cont = letCont.continuation; | 133 _removeContinuation(cont); |
| 116 _removeNode(letCont); | |
| 117 | 134 |
| 118 // Replace its invocation with the continuation body. | 135 // Replace its invocation with the continuation body. |
| 119 InvokeContinuation invoke = cont.firstRef.parent; | 136 InvokeContinuation invoke = cont.firstRef.parent; |
| 120 InteriorNode invokeParent = invoke.parent; | 137 InteriorNode invokeParent = invoke.parent; |
| 121 | 138 |
| 122 cont.body.parent = invokeParent; | 139 cont.body.parent = invokeParent; |
| 123 invokeParent.body = cont.body; | 140 invokeParent.body = cont.body; |
| 124 | 141 |
| 125 // Substitute the invocation argument for the continuation parameter. | 142 // Substitute the invocation argument for the continuation parameter. |
| 126 for (int i = 0; i < invoke.arguments.length; i++) { | 143 for (int i = 0; i < invoke.arguments.length; i++) { |
| 127 Reference argRef = invoke.arguments[i]; | 144 Reference argRef = invoke.arguments[i]; |
| 128 argRef.definition.substituteFor(cont.parameters[i]); | 145 argRef.definition.substituteFor(cont.parameters[i]); |
| 129 } | 146 } |
| 130 | 147 |
| 131 // Perform bookkeeping on removed body and scan for new redexes. | 148 // Perform bookkeeping on substituted body and scan for new redexes. |
| 132 new _RemovalRedexVisitor(_worklist).visit(invoke); | 149 new _RemovalVisitor(_worklist).visit(invoke); |
| 133 } | 150 } |
| 134 | 151 |
| 135 /// Applies the eta-cont reduction: | 152 /// Applies the eta-cont reduction: |
| 136 /// letcont k x = j x in E -> E[j/k]. | 153 /// letcont k x = j x in E -> E[j/k]. |
| 137 /// If k is unused, degenerates to dead-cont. | 154 /// If k is unused, degenerates to dead-cont. |
| 138 void _reduceEtaCont(_ReductionTask task) { | 155 void _reduceEtaCont(_ReductionTask task) { |
| 139 // Might have been mutated, recheck if reduction is still valid. | 156 // Might have been mutated, recheck if reduction is still valid. |
| 140 // In the following example, the eta-cont reduction of k1 could have been | 157 // In the following example, the eta-cont reduction of k1 could have been |
| 141 // invalidated by an earlier beta-cont-lin reduction of k0. | 158 // invalidated by an earlier beta-cont-lin reduction of k0. |
| 142 // | 159 // |
| 143 // letcont k0 x0 = E0 in | 160 // letcont k0 x0 = E0 in |
| 144 // letcont k1 x1 = k0 x1 in E1 | 161 // letcont k1 x1 = k0 x1 in E1 |
| 145 if (!_isEtaCont(task.node)) { | 162 if (!_isEtaCont(task.node)) { |
| 146 return; | 163 return; |
| 147 } | 164 } |
| 148 | 165 |
| 149 // Remove the continuation. | 166 // Remove the continuation. |
| 150 LetCont letCont = task.node; | 167 Continuation cont = task.node; |
| 151 Continuation cont = letCont.continuation; | 168 _removeContinuation(cont); |
| 152 _removeNode(letCont); | |
| 153 | 169 |
| 154 InvokeContinuation invoke = cont.body; | 170 InvokeContinuation invoke = cont.body; |
| 155 Continuation wrappedCont = invoke.continuation.definition; | 171 Continuation wrappedCont = invoke.continuation.definition; |
| 156 | 172 |
| 157 // Replace all occurrences with the wrapped continuation. | 173 // Replace all occurrences with the wrapped continuation. |
| 158 wrappedCont.substituteFor(cont); | 174 wrappedCont.substituteFor(cont); |
| 159 | 175 |
| 160 // Perform bookkeeping on removed body and scan for new redexes. | 176 // Perform bookkeeping on removed body and scan for new redexes. |
| 161 new _RemovalRedexVisitor(_worklist).visit(cont); | 177 new _RemovalVisitor(_worklist).visit(cont); |
| 162 } | 178 } |
| 163 } | 179 } |
| 164 | 180 |
| 165 /// Returns true iff the bound primitive is unused. | 181 /// Returns true iff the bound primitive is unused. |
| 166 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; | 182 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; |
| 167 | 183 |
| 168 /// Returns true iff the bound continuation is unused. | 184 /// Returns true iff the continuation is unused. |
| 169 bool _isDeadCont(LetCont node) => !node.continuation.hasAtLeastOneUse; | 185 bool _isDeadCont(Continuation cont) { |
| 186 assert(!cont.isReturnContinuation); |
| 187 return !cont.hasAtLeastOneUse; |
| 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 22 matching lines...) Expand all Loading... |
| 225 return true; | 241 return true; |
| 226 } | 242 } |
| 227 | 243 |
| 228 /// Traverses a term and adds any found redexes to the worklist. | 244 /// Traverses a term and adds any found redexes to the worklist. |
| 229 class _RedexVisitor extends RecursiveVisitor { | 245 class _RedexVisitor extends RecursiveVisitor { |
| 230 final Set<_ReductionTask> worklist; | 246 final Set<_ReductionTask> worklist; |
| 231 | 247 |
| 232 _RedexVisitor(this.worklist); | 248 _RedexVisitor(this.worklist); |
| 233 | 249 |
| 234 void processLetPrim(LetPrim node) { | 250 void processLetPrim(LetPrim node) { |
| 235 if (node.parent == ShrinkingReducer._DELETED) { | 251 if (_isDeadVal(node)) { |
| 236 return; | |
| 237 } else if (_isDeadVal(node)) { | |
| 238 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); | 252 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); |
| 239 } | 253 } |
| 240 } | 254 } |
| 241 | 255 |
| 242 void processLetCont(LetCont node) { | 256 void processContinuation(Continuation node) { |
| 243 if (node.parent == ShrinkingReducer._DELETED) { | 257 if (_isDeadCont(node)) { |
| 244 return; | |
| 245 } else if (_isDeadCont(node)) { | |
| 246 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); | 258 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
| 247 } else if (_isEtaCont(node)) { | 259 } else if (_isEtaCont(node)) { |
| 248 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); | 260 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
| 249 } else if (_isBetaContLin(node)){ | 261 } else if (_isBetaContLin(node)){ |
| 250 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | 262 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); |
| 251 } | 263 } |
| 252 } | 264 } |
| 253 } | 265 } |
| 254 | 266 |
| 255 /// Traverses a deleted CPS term, marking existing tasks associated with a node | 267 /// Traverses a deleted CPS term, marking nodes that might participate in a |
| 256 /// within the term as deleted (which causes them to be skipped lazily when | 268 /// redex as deleted and adding newly created redexes to the worklist. |
| 257 /// popped from the worklist), and adding newly created redexes to the worklist. | 269 /// |
| 258 class _RemovalRedexVisitor extends _RedexVisitor { | 270 /// Deleted nodes that might participate in a reduction task are marked so that |
| 259 _RemovalRedexVisitor(Set<_ReductionTask> worklist) : super(worklist); | 271 /// any corresponding tasks can be skipped. Nodes are marked so by setting |
| 272 /// their parent to the deleted sentinel. |
| 273 class _RemovalVisitor extends RecursiveVisitor { |
| 274 final Set<_ReductionTask> worklist; |
| 275 |
| 276 _RemovalVisitor(this.worklist); |
| 260 | 277 |
| 261 void processLetPrim(LetPrim node) { | 278 void processLetPrim(LetPrim node) { |
| 262 node.parent = ShrinkingReducer._DELETED; | 279 node.parent = ShrinkingReducer._DELETED; |
| 263 } | 280 } |
| 264 | 281 |
| 265 void processLetCont(LetCont node) { | 282 void processContinuation(Continuation node) { |
| 266 node.parent = ShrinkingReducer._DELETED; | 283 node.parent = ShrinkingReducer._DELETED; |
| 267 } | 284 } |
| 268 | 285 |
| 269 void processReference(Reference reference) { | 286 void processReference(Reference reference) { |
| 270 reference.unlink(); | 287 reference.unlink(); |
| 271 | 288 |
| 272 if (reference.definition is Primitive) { | 289 if (reference.definition is Primitive) { |
| 273 Primitive primitive = reference.definition; | 290 Primitive primitive = reference.definition; |
| 274 Node parent = primitive.parent; | 291 Node parent = primitive.parent; |
| 292 // The parent might be the deleted sentinel, or it might be a |
| 293 // Continuation or FunctionDefinition if the primitive is an argument. |
| 275 if (parent is LetPrim && _isDeadVal(parent)) { | 294 if (parent is LetPrim && _isDeadVal(parent)) { |
| 276 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 295 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 277 } | 296 } |
| 278 } else if (reference.definition is Continuation) { | 297 } else if (reference.definition is Continuation) { |
| 279 Continuation cont = reference.definition; | 298 Continuation cont = reference.definition; |
| 280 if (cont.isRecursive && cont.hasAtMostOneUse) { | |
| 281 // Convert recursive to nonrecursive continuations. | |
| 282 // If the continuation is still in use, it is either dead and will be | |
| 283 // removed, or it is called nonrecursively outside its body. | |
| 284 cont.isRecursive = false; | |
| 285 } | |
| 286 Node parent = cont.parent; | 299 Node parent = cont.parent; |
| 287 if (parent is LetCont && _isDeadCont(parent)) { | 300 // The parent might be the deleted sentinel, or it might be a |
| 288 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, parent)); | 301 // FunctionDefinition if the continuation is the return continuation. |
| 302 if (parent is LetCont) { |
| 303 if (cont.isRecursive && cont.hasAtMostOneUse) { |
| 304 // Convert recursive to nonrecursive continuations. If the |
| 305 // continuation is still in use, it is either dead and will be |
| 306 // removed, or it is called nonrecursively outside its body. |
| 307 cont.isRecursive = false; |
| 308 } |
| 309 if (_isDeadCont(cont)) { |
| 310 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); |
| 311 } |
| 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 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 class _ReductionTask { | 485 class _ReductionTask { |
| 460 final _ReductionKind kind; | 486 final _ReductionKind kind; |
| 461 final Node node; | 487 final Node node; |
| 462 | 488 |
| 463 int get hashCode { | 489 int get hashCode { |
| 464 assert(kind.hashCode < (1 << 2)); | 490 assert(kind.hashCode < (1 << 2)); |
| 465 return (node.hashCode << 2) | kind.hashCode; | 491 return (node.hashCode << 2) | kind.hashCode; |
| 466 } | 492 } |
| 467 | 493 |
| 468 _ReductionTask(this.kind, this.node) { | 494 _ReductionTask(this.kind, this.node) { |
| 469 // If new node types are added, they must be marked as deleted in | 495 assert(node is Continuation || node is LetPrim); |
| 470 // [[_RemovalRedexVisitor]]. | |
| 471 assert(node is LetCont || node is LetPrim); | |
| 472 } | 496 } |
| 473 | 497 |
| 474 bool operator==(_ReductionTask that) { | 498 bool operator==(_ReductionTask that) { |
| 475 return (that.kind == this.kind && that.node == this.node); | 499 return (that.kind == this.kind && that.node == this.node); |
| 476 } | 500 } |
| 477 | 501 |
| 478 String toString() => "$kind: $node"; | 502 String toString() => "$kind: $node"; |
| 479 } | 503 } |
| 480 | 504 |
| 481 /// A dummy class used solely to mark nodes as deleted once they are removed | 505 /// A dummy class used solely to mark nodes as deleted once they are removed |
| 482 /// from a term. | 506 /// from a term. |
| 483 class _DeletedNode extends Node { | 507 class _DeletedNode extends Node { |
| 484 accept(_) => null; | 508 accept(_) => null; |
| 485 } | 509 } |
| OLD | NEW |