| 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 library dart2js.cps_ir.shrinking_reductions; | 5 library dart2js.cps_ir.shrinking_reductions; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import 'optimizers.dart'; | 8 import 'optimizers.dart'; |
| 9 import 'cps_fragment.dart'; | |
| 10 import '../constants/values.dart' as values; | |
| 11 | 9 |
| 12 /** | 10 /** |
| 13 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described | 11 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described |
| 14 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. | 12 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. |
| 15 */ | 13 */ |
| 16 class ShrinkingReducer extends Pass { | 14 class ShrinkingReducer extends Pass { |
| 17 String get passName => 'Shrinking reductions'; | 15 String get passName => 'Shrinking reductions'; |
| 18 | 16 |
| 19 List<_ReductionTask> _worklist; | 17 final List<_ReductionTask> _worklist = new List<_ReductionTask>(); |
| 20 | |
| 21 static final _DeletedNode _DELETED = new _DeletedNode(); | |
| 22 | 18 |
| 23 /// Applies shrinking reductions to root, mutating root in the process. | 19 /// Applies shrinking reductions to root, mutating root in the process. |
| 24 @override | 20 @override |
| 25 void rewrite(FunctionDefinition root) { | 21 void rewrite(FunctionDefinition root) { |
| 26 _worklist = new List<_ReductionTask>(); | |
| 27 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist); | 22 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist); |
| 28 | 23 |
| 29 // Sweep over the term, collecting redexes into the worklist. | 24 // Sweep over the term, collecting redexes into the worklist. |
| 30 redexVisitor.visit(root); | 25 redexVisitor.visit(root); |
| 31 | 26 |
| 32 // Process the worklist. | 27 _iterateWorklist(); |
| 28 } |
| 29 |
| 30 void _iterateWorklist() { |
| 33 while (_worklist.isNotEmpty) { | 31 while (_worklist.isNotEmpty) { |
| 34 _ReductionTask task = _worklist.removeLast(); | 32 _ReductionTask task = _worklist.removeLast(); |
| 35 _processTask(task); | 33 _processTask(task); |
| 36 } | 34 } |
| 37 } | 35 } |
| 38 | 36 |
| 37 /// Call instead of [_iterateWorklist] to check at every step that no |
| 38 /// redex was missed. |
| 39 void _debugWorklist(FunctionDefinition root) { |
| 40 while (_worklist.isNotEmpty) { |
| 41 _ReductionTask task = _worklist.removeLast(); |
| 42 String irBefore = root.debugString({ |
| 43 task.node: '${task.kind} applied here' |
| 44 }); |
| 45 _processTask(task); |
| 46 Set seenRedexes = _worklist.where(isValidTask).toSet(); |
| 47 Set actualRedexes = (new _RedexVisitor([])..visit(root)).worklist.toSet(); |
| 48 if (!seenRedexes.containsAll(actualRedexes)) { |
| 49 _ReductionTask missedTask = |
| 50 actualRedexes.firstWhere((x) => !seenRedexes.contains(x)); |
| 51 print('\nBEFORE $task:\n'); |
| 52 print(irBefore); |
| 53 print('\nAFTER $task:\n'); |
| 54 root.debugPrint({ |
| 55 missedTask.node: 'MISSED ${missedTask.kind}' |
| 56 }); |
| 57 throw 'Missed $missedTask after processing $task'; |
| 58 } |
| 59 } |
| 60 } |
| 61 |
| 62 bool isValidTask(_ReductionTask task) { |
| 63 switch (task.kind) { |
| 64 case _ReductionKind.DEAD_VAL: |
| 65 return _isDeadVal(task.node); |
| 66 case _ReductionKind.DEAD_CONT: |
| 67 return _isDeadCont(task.node); |
| 68 case _ReductionKind.BETA_CONT_LIN: |
| 69 return _isBetaContLin(task.node); |
| 70 case _ReductionKind.ETA_CONT: |
| 71 return _isEtaCont(task.node); |
| 72 case _ReductionKind.DEAD_PARAMETER: |
| 73 return _isDeadParameter(task.node); |
| 74 case _ReductionKind.BRANCH: |
| 75 return _isBranchRedex(task.node); |
| 76 } |
| 77 } |
| 78 |
| 39 /// Removes the given node from the CPS graph, replacing it with its body | 79 /// Removes the given node from the CPS graph, replacing it with its body |
| 40 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. | 80 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. |
| 41 void _removeNode(InteriorNode node) { | 81 void _removeNode(InteriorNode node) { |
| 42 Node body = node.body; | 82 Node body = node.body; |
| 43 InteriorNode parent = node.parent; | 83 InteriorNode parent = node.parent; |
| 44 assert(parent.body == node); | 84 assert(parent.body == node); |
| 45 | 85 |
| 46 body.parent = parent; | 86 body.parent = parent; |
| 47 parent.body = body; | 87 parent.body = body; |
| 48 node.parent = _DELETED; | 88 node.parent = null; |
| 89 |
| 90 // The removed node could be the last node between a continuation and |
| 91 // an InvokeContinuation in the body. |
| 92 if (parent is Continuation) { |
| 93 _checkEtaCont(parent); |
| 94 _checkUselessBranchTarget(parent); |
| 95 } |
| 49 } | 96 } |
| 50 | 97 |
| 51 /// Remove a given continuation from the CPS graph. The LetCont itself is | 98 /// Remove a given continuation from the CPS graph. The LetCont itself is |
| 52 /// removed if the given continuation is the only binding. | 99 /// removed if the given continuation is the only binding. |
| 53 void _removeContinuation(Continuation cont) { | 100 void _removeContinuation(Continuation cont) { |
| 54 LetCont parent = cont.parent; | 101 LetCont parent = cont.parent; |
| 55 if (parent.continuations.length == 1) { | 102 if (parent.continuations.length == 1) { |
| 56 _removeNode(parent); | 103 _removeNode(parent); |
| 57 } else { | 104 } else { |
| 58 parent.continuations.remove(cont); | 105 parent.continuations.remove(cont); |
| 59 } | 106 } |
| 60 cont.parent = _DELETED; | 107 cont.parent = null; |
| 61 } | 108 } |
| 62 | 109 |
| 63 void _processTask(_ReductionTask task) { | 110 void _processTask(_ReductionTask task) { |
| 64 // Skip tasks for deleted nodes. | 111 // Skip tasks for deleted nodes. |
| 65 if (task.node.parent == _DELETED) { | 112 if (task.node.parent == null) { |
| 66 return; | 113 return; |
| 67 } | 114 } |
| 68 | 115 |
| 69 switch (task.kind) { | 116 switch (task.kind) { |
| 70 case _ReductionKind.DEAD_VAL: | 117 case _ReductionKind.DEAD_VAL: |
| 71 _reduceDeadVal(task); | 118 _reduceDeadVal(task); |
| 72 break; | 119 break; |
| 73 case _ReductionKind.DEAD_CONT: | 120 case _ReductionKind.DEAD_CONT: |
| 74 _reduceDeadCont(task); | 121 _reduceDeadCont(task); |
| 75 break; | 122 break; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 86 _reduceBranch(task); | 133 _reduceBranch(task); |
| 87 break; | 134 break; |
| 88 default: | 135 default: |
| 89 assert(false); | 136 assert(false); |
| 90 } | 137 } |
| 91 } | 138 } |
| 92 | 139 |
| 93 /// Applies the dead-val reduction: | 140 /// Applies the dead-val reduction: |
| 94 /// letprim x = V in E -> E (x not free in E). | 141 /// letprim x = V in E -> E (x not free in E). |
| 95 void _reduceDeadVal(_ReductionTask task) { | 142 void _reduceDeadVal(_ReductionTask task) { |
| 143 if (_isRemoved(task.node)) return; |
| 96 assert(_isDeadVal(task.node)); | 144 assert(_isDeadVal(task.node)); |
| 97 | 145 |
| 98 // Remove dead primitive. | 146 LetPrim deadLet = task.node; |
| 99 LetPrim letPrim = task.node; | 147 Primitive deadPrim = deadLet.primitive; |
| 100 destroyRefinementsOfDeadPrimitive(letPrim.primitive); | 148 assert(deadPrim.hasNoRefinedUses); |
| 101 _removeNode(letPrim); | 149 // The node has no effective uses but can have refinement uses, which |
| 150 // themselves can have more refinements uses (but only refinement uses). |
| 151 // We must remove the entire refinement tree while looking for redexes |
| 152 // whenever we remove one. |
| 153 List<Primitive> deadlist = <Primitive>[deadPrim]; |
| 154 while (deadlist.isNotEmpty) { |
| 155 Primitive node = deadlist.removeLast(); |
| 156 while (node.firstRef != null) { |
| 157 Reference ref = node.firstRef; |
| 158 Refinement use = ref.parent; |
| 159 deadlist.add(use); |
| 160 ref.unlink(); |
| 161 } |
| 162 LetPrim binding = node.parent; |
| 163 _removeNode(binding); // Remove the binding and check for eta redexes. |
| 164 } |
| 102 | 165 |
| 103 // Perform bookkeeping on removed body and scan for new redexes. | 166 // Perform bookkeeping on removed body and scan for new redexes. |
| 104 new _RemovalVisitor(_worklist).visit(letPrim.primitive); | 167 new _RemovalVisitor(_worklist).visit(deadPrim); |
| 105 } | 168 } |
| 106 | 169 |
| 107 /// Applies the dead-cont reduction: | 170 /// Applies the dead-cont reduction: |
| 108 /// letcont k x = E0 in E1 -> E1 (k not free in E1). | 171 /// letcont k x = E0 in E1 -> E1 (k not free in E1). |
| 109 void _reduceDeadCont(_ReductionTask task) { | 172 void _reduceDeadCont(_ReductionTask task) { |
| 110 assert(_isDeadCont(task.node)); | 173 assert(_isDeadCont(task.node)); |
| 111 | 174 |
| 112 // Remove dead continuation. | 175 // Remove dead continuation. |
| 113 Continuation cont = task.node; | 176 Continuation cont = task.node; |
| 114 _removeContinuation(cont); | 177 _removeContinuation(cont); |
| 115 | 178 |
| 116 // Perform bookkeeping on removed body and scan for new redexes. | 179 // Perform bookkeeping on removed body and scan for new redexes. |
| 117 new _RemovalVisitor(_worklist).visit(cont); | 180 new _RemovalVisitor(_worklist).visit(cont); |
| 118 } | 181 } |
| 119 | 182 |
| 120 /// Applies the beta-cont-lin reduction: | 183 /// Applies the beta-cont-lin reduction: |
| 121 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). | 184 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). |
| 122 void _reduceBetaContLin(_ReductionTask task) { | 185 void _reduceBetaContLin(_ReductionTask task) { |
| 123 // Might have been mutated, recheck if reduction is still valid. | 186 // Might have been mutated, recheck if reduction is still valid. |
| 124 // In the following example, the beta-cont-lin reduction of k0 could have | 187 // In the following example, the beta-cont-lin reduction of k0 could have |
| 125 // been invalidated by removal of the dead continuation k1: | 188 // been invalidated by removal of the dead continuation k1: |
| 126 // | 189 // |
| 127 // letcont k0 x0 = E0 in | 190 // letcont k0 x0 = E0 in |
| 128 // letcont k1 x1 = k0 x1 in | 191 // letcont k1 x1 = k0 x1 in |
| 129 // return x2 | 192 // return x2 |
| 130 if (!_isBetaContLin(task.node)) { | 193 if (!_isBetaContLin(task.node)) { |
| 131 return; | 194 return; |
| 132 } | 195 } |
| 133 | 196 |
| 134 // Remove the continuation. | |
| 135 Continuation cont = task.node; | 197 Continuation cont = task.node; |
| 136 _removeContinuation(cont); | |
| 137 | |
| 138 // Replace its invocation with the continuation body. | |
| 139 InvokeContinuation invoke = cont.firstRef.parent; | 198 InvokeContinuation invoke = cont.firstRef.parent; |
| 140 InteriorNode invokeParent = invoke.parent; | 199 InteriorNode invokeParent = invoke.parent; |
| 200 Expression body = cont.body; |
| 141 | 201 |
| 142 cont.body.parent = invokeParent; | 202 // Replace the invocation with the continuation body. |
| 143 invokeParent.body = cont.body; | 203 invokeParent.body = body; |
| 204 body.parent = invokeParent; |
| 205 cont.body = null; |
| 144 | 206 |
| 145 // Substitute the invocation argument for the continuation parameter. | 207 // Substitute the invocation argument for the continuation parameter. |
| 146 for (int i = 0; i < invoke.arguments.length; i++) { | 208 for (int i = 0; i < invoke.arguments.length; i++) { |
| 147 cont.parameters[i].replaceUsesWith(invoke.arguments[i].definition); | 209 Parameter param = cont.parameters[i]; |
| 148 invoke.arguments[i].definition.useElementAsHint(cont.parameters[i].hint); | 210 Primitive argument = invoke.arguments[i].definition; |
| 211 param.replaceUsesWith(argument); |
| 212 argument.useElementAsHint(param.hint); |
| 213 _checkConstantBranchCondition(argument); |
| 149 } | 214 } |
| 150 | 215 |
| 216 // Remove the continuation after inlining it so we can check for eta redexes |
| 217 // which may arise after removing the LetCont. |
| 218 _removeContinuation(cont); |
| 219 |
| 151 // Perform bookkeeping on substituted body and scan for new redexes. | 220 // Perform bookkeeping on substituted body and scan for new redexes. |
| 152 new _RemovalVisitor(_worklist).visit(invoke); | 221 new _RemovalVisitor(_worklist).visit(invoke); |
| 222 |
| 223 if (invokeParent is Continuation) { |
| 224 _checkEtaCont(invokeParent); |
| 225 _checkUselessBranchTarget(invokeParent); |
| 226 } |
| 153 } | 227 } |
| 154 | 228 |
| 155 /// Applies the eta-cont reduction: | 229 /// Applies the eta-cont reduction: |
| 156 /// letcont k x = j x in E -> E[j/k]. | 230 /// letcont k x = j x in E -> E[j/k]. |
| 157 /// If k is unused, degenerates to dead-cont. | 231 /// If k is unused, degenerates to dead-cont. |
| 158 void _reduceEtaCont(_ReductionTask task) { | 232 void _reduceEtaCont(_ReductionTask task) { |
| 159 // Might have been mutated, recheck if reduction is still valid. | 233 // Might have been mutated, recheck if reduction is still valid. |
| 160 // In the following example, the eta-cont reduction of k1 could have been | 234 // In the following example, the eta-cont reduction of k1 could have been |
| 161 // invalidated by an earlier beta-cont-lin reduction of k0. | 235 // invalidated by an earlier beta-cont-lin reduction of k0. |
| 162 // | 236 // |
| (...skipping 18 matching lines...) Expand all Loading... |
| 181 // cont will be as well, after the reduction. | 255 // cont will be as well, after the reduction. |
| 182 if (invoke.isEscapingTry) { | 256 if (invoke.isEscapingTry) { |
| 183 Reference current = cont.firstRef; | 257 Reference current = cont.firstRef; |
| 184 while (current != null) { | 258 while (current != null) { |
| 185 InvokeContinuation owner = current.parent; | 259 InvokeContinuation owner = current.parent; |
| 186 owner.isEscapingTry = true; | 260 owner.isEscapingTry = true; |
| 187 current = current.next; | 261 current = current.next; |
| 188 } | 262 } |
| 189 } | 263 } |
| 190 | 264 |
| 191 // Replace all occurrences with the wrapped continuation. | 265 // Replace all occurrences with the wrapped continuation and find redexes. |
| 192 cont.replaceUsesWith(wrappedCont); | 266 while (cont.firstRef != null) { |
| 267 Reference ref = cont.firstRef; |
| 268 ref.changeTo(wrappedCont); |
| 269 Node use = ref.parent; |
| 270 if (use is InvokeContinuation && use.parent is Continuation) { |
| 271 _checkUselessBranchTarget(use.parent); |
| 272 } |
| 273 } |
| 193 | 274 |
| 194 // Perform bookkeeping on removed body and scan for new redexes. | 275 // Perform bookkeeping on removed body and scan for new redexes. |
| 195 new _RemovalVisitor(_worklist).visit(cont); | 276 new _RemovalVisitor(_worklist).visit(cont); |
| 196 } | 277 } |
| 197 | 278 |
| 198 void _reduceBranch(_ReductionTask task) { | 279 void _reduceBranch(_ReductionTask task) { |
| 199 Branch branch = task.node; | 280 Branch branch = task.node; |
| 200 // Replace Branch with InvokeContinuation of one of the targets. When the | 281 // Replace Branch with InvokeContinuation of one of the targets. When the |
| 201 // branch is deleted the other target becomes unreferenced and the chosen | 282 // branch is deleted the other target becomes unreferenced and the chosen |
| 202 // target becomes available for eta-cont and further reductions. | 283 // target becomes available for eta-cont and further reductions. |
| 203 Continuation target; | 284 Continuation target; |
| 204 Primitive condition = branch.condition.definition; | 285 Primitive condition = branch.condition.definition; |
| 205 if (condition is Constant) { | 286 if (condition is Constant) { |
| 206 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck) | 287 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck) |
| 207 ? branch.trueContinuation.definition | 288 ? branch.trueContinuation.definition |
| 208 : branch.falseContinuation.definition; | 289 : branch.falseContinuation.definition; |
| 209 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation.definition)) { | 290 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation.definition)) { |
| 210 target = branch.trueContinuation.definition; | 291 target = branch.trueContinuation.definition; |
| 211 } else { | 292 } else { |
| 212 return; | 293 return; |
| 213 } | 294 } |
| 214 | 295 |
| 215 InvokeContinuation invoke = new InvokeContinuation( | 296 InvokeContinuation invoke = new InvokeContinuation( |
| 216 target, <Primitive>[] | 297 target, <Primitive>[] |
| 217 // TODO(sra): Add sourceInformation. | 298 // TODO(sra): Add sourceInformation. |
| 218 /*, sourceInformation: branch.sourceInformation*/); | 299 /*, sourceInformation: branch.sourceInformation*/); |
| 219 branch.parent.body = invoke; | 300 branch.parent.body = invoke; |
| 220 invoke.parent = branch.parent; | 301 invoke.parent = branch.parent; |
| 221 branch.parent = _DELETED; | 302 branch.parent = null; |
| 222 | 303 |
| 223 new _RemovalVisitor(_worklist).visit(branch); | 304 new _RemovalVisitor(_worklist).visit(branch); |
| 224 } | 305 } |
| 225 | 306 |
| 226 void _reduceDeadParameter(_ReductionTask task) { | 307 void _reduceDeadParameter(_ReductionTask task) { |
| 227 // Continuation eta-reduction can destroy a dead parameter redex. For | 308 // Continuation eta-reduction can destroy a dead parameter redex. For |
| 228 // example, in the term: | 309 // example, in the term: |
| 229 // | 310 // |
| 230 // let cont k0(v0) = /* v0 is not used */ in | 311 // let cont k0(v0) = /* v0 is not used */ in |
| 231 // let cont k1(v1) = k0(v1) in | 312 // let cont k1(v1) = k0(v1) in |
| 232 // call foo () k1 | 313 // call foo () k1 |
| 233 // | 314 // |
| 234 // Continuation eta-reduction of k1 gives: | 315 // Continuation eta-reduction of k1 gives: |
| 235 // | 316 // |
| 236 // let cont k0(v0) = /* v0 is not used */ in | 317 // let cont k0(v0) = /* v0 is not used */ in |
| 237 // call foo () k0 | 318 // call foo () k0 |
| 238 // | 319 // |
| 239 // Where the dead parameter reduction is no longer valid because we do not | 320 // Where the dead parameter reduction is no longer valid because we do not |
| 240 // allow removing the paramter of call continuations. We disallow such eta | 321 // allow removing the paramter of call continuations. We disallow such eta |
| 241 // reductions in [_isEtaCont]. | 322 // reductions in [_isEtaCont]. |
| 242 assert(_isDeadParameter(task.node)); | 323 Parameter parameter = task.node; |
| 324 if (_isParameterRemoved(parameter)) return; |
| 325 assert(_isDeadParameter(parameter)); |
| 243 | 326 |
| 244 Parameter parameter = task.node; | |
| 245 Continuation continuation = parameter.parent; | 327 Continuation continuation = parameter.parent; |
| 246 int index = continuation.parameters.indexOf(parameter); | 328 int index = continuation.parameters.indexOf(parameter); |
| 247 assert(index != -1); | 329 assert(index != -1); |
| 330 continuation.parameters.removeAt(index); |
| 331 parameter.parent = null; // Mark as removed. |
| 248 | 332 |
| 249 // Remove the index'th argument from each invocation. | 333 // Remove the index'th argument from each invocation. |
| 250 Reference<Continuation> current = continuation.firstRef; | 334 for (Reference ref = continuation.firstRef; ref != null; ref = ref.next) { |
| 251 while (current != null) { | 335 InvokeContinuation invoke = ref.parent; |
| 252 InvokeContinuation invoke = current.parent; | |
| 253 Reference<Primitive> argument = invoke.arguments[index]; | 336 Reference<Primitive> argument = invoke.arguments[index]; |
| 254 argument.unlink(); | 337 argument.unlink(); |
| 255 // Removing an argument can create a dead parameter or dead value redex. | 338 invoke.arguments.removeAt(index); |
| 256 if (argument.definition is Parameter) { | 339 // Removing an argument can create a dead primitive or an eta-redex |
| 257 if (_isDeadParameter(argument.definition)) { | 340 // in case the parent is a continuation that now has matching parameters. |
| 258 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, | 341 _checkDeadPrimitive(argument.definition); |
| 259 argument.definition)); | 342 if (invoke.parent is Continuation) { |
| 260 } | 343 _checkEtaCont(invoke.parent); |
| 261 } else { | 344 _checkUselessBranchTarget(invoke.parent); |
| 262 Node parent = argument.definition.parent; | |
| 263 if (parent is LetPrim) { | |
| 264 if (_isDeadVal(parent)) { | |
| 265 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | |
| 266 } | |
| 267 } | |
| 268 } | 345 } |
| 269 invoke.arguments.removeAt(index); | |
| 270 current = current.next; | |
| 271 } | 346 } |
| 272 continuation.parameters.removeAt(index); | |
| 273 | 347 |
| 274 // Removing an unused parameter can create an eta-redex. | 348 // Removing an unused parameter can create an eta-redex, in case the |
| 349 // body is an InvokeContinuation that now has matching arguments. |
| 350 _checkEtaCont(continuation); |
| 351 } |
| 352 |
| 353 void _checkEtaCont(Continuation continuation) { |
| 275 if (_isEtaCont(continuation)) { | 354 if (_isEtaCont(continuation)) { |
| 276 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); | 355 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); |
| 277 } | 356 } |
| 278 } | 357 } |
| 358 |
| 359 void _checkUselessBranchTarget(Continuation continuation) { |
| 360 if (_isBranchTargetOfUselessIf(continuation)) { |
| 361 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, |
| 362 continuation.firstRef.parent)); |
| 363 } |
| 364 } |
| 365 |
| 366 void _checkConstantBranchCondition(Primitive primitive) { |
| 367 if (primitive is! Constant) return; |
| 368 for (Reference ref = primitive.firstRef; ref != null; ref = ref.next) { |
| 369 Node use = ref.parent; |
| 370 if (use is Branch) { |
| 371 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, use)); |
| 372 } |
| 373 } |
| 374 } |
| 375 |
| 376 void _checkDeadPrimitive(Primitive primitive) { |
| 377 primitive = primitive.unrefined; |
| 378 if (primitive is Parameter) { |
| 379 if (_isDeadParameter(primitive)) { |
| 380 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, |
| 381 primitive)); |
| 382 } |
| 383 } else if (primitive.parent is LetPrim) { |
| 384 LetPrim letPrim = primitive.parent; |
| 385 if (_isDeadVal(letPrim)) { |
| 386 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, letPrim)); |
| 387 } |
| 388 } |
| 389 } |
| 390 } |
| 391 |
| 392 bool _isRemoved(InteriorNode node) { |
| 393 return node.parent == null; |
| 394 } |
| 395 |
| 396 bool _isParameterRemoved(Parameter parameter) { |
| 397 // A parameter can be removed directly or because its continuation is removed. |
| 398 return parameter.parent == null || _isRemoved(parameter.parent); |
| 279 } | 399 } |
| 280 | 400 |
| 281 /// Returns true iff the bound primitive is unused, and has no effects | 401 /// Returns true iff the bound primitive is unused, and has no effects |
| 282 /// preventing it from being eliminated. | 402 /// preventing it from being eliminated. |
| 283 bool _isDeadVal(LetPrim node) { | 403 bool _isDeadVal(LetPrim node) { |
| 284 return node.primitive.hasNoEffectiveUses && | 404 return !_isRemoved(node) && |
| 405 node.primitive.hasNoRefinedUses && |
| 285 node.primitive.isSafeForElimination; | 406 node.primitive.isSafeForElimination; |
| 286 } | 407 } |
| 287 | 408 |
| 288 /// Returns true iff the continuation is unused. | 409 /// Returns true iff the continuation is unused. |
| 289 bool _isDeadCont(Continuation cont) { | 410 bool _isDeadCont(Continuation cont) { |
| 290 return !cont.isReturnContinuation && !cont.hasAtLeastOneUse; | 411 return !_isRemoved(cont) && |
| 412 !cont.isReturnContinuation && |
| 413 !cont.hasAtLeastOneUse; |
| 291 } | 414 } |
| 292 | 415 |
| 293 /// Returns true iff the continuation has a body (i.e., it is not the return | 416 /// Returns true iff the continuation has a body (i.e., it is not the return |
| 294 /// continuation), it is used exactly once, and that use is as the continuation | 417 /// continuation), it is used exactly once, and that use is as the continuation |
| 295 /// of a continuation invocation. | 418 /// of a continuation invocation. |
| 296 bool _isBetaContLin(Continuation cont) { | 419 bool _isBetaContLin(Continuation cont) { |
| 420 if (_isRemoved(cont)) return false; |
| 421 |
| 297 // There is a restriction on continuation eta-redexes that the body is not an | 422 // There is a restriction on continuation eta-redexes that the body is not an |
| 298 // invocation of the return continuation, because that leads to worse code | 423 // invocation of the return continuation, because that leads to worse code |
| 299 // when translating back to direct style (it duplicates returns). There is no | 424 // when translating back to direct style (it duplicates returns). There is no |
| 300 // such restriction here because continuation beta-reduction is only performed | 425 // such restriction here because continuation beta-reduction is only performed |
| 301 // for singly referenced continuations. Thus, there is no possibility of code | 426 // for singly referenced continuations. Thus, there is no possibility of code |
| 302 // duplication. | 427 // duplication. |
| 303 if (cont.isReturnContinuation || !cont.hasExactlyOneUse) { | 428 if (cont.isReturnContinuation || !cont.hasExactlyOneUse) { |
| 304 return false; | 429 return false; |
| 305 } | 430 } |
| 306 | 431 |
| 307 if (cont.firstRef.parent is! InvokeContinuation) return false; | 432 if (cont.firstRef.parent is! InvokeContinuation) return false; |
| 308 | 433 |
| 309 InvokeContinuation invoke = cont.firstRef.parent; | 434 InvokeContinuation invoke = cont.firstRef.parent; |
| 310 if (cont != invoke.continuation.definition) return false; | |
| 311 | 435 |
| 312 // Beta-reduction will move the continuation's body to its unique invocation | 436 // Beta-reduction will move the continuation's body to its unique invocation |
| 313 // site. This is not safe if the body is moved into an exception handler | 437 // site. This is not safe if the body is moved into an exception handler |
| 314 // binding. Search from the invocation to the continuation binding to | 438 // binding. |
| 315 // make sure that there is no binding for a handler. | 439 if (invoke.isEscapingTry) return false; |
| 316 Node current = invoke.parent; | 440 |
| 317 while (current != cont.parent) { | |
| 318 // There is no need to reduce a beta-redex inside a deleted subterm. | |
| 319 if (current == ShrinkingReducer._DELETED) return false; | |
| 320 if (current is LetHandler) return false; | |
| 321 current = current.parent; | |
| 322 } | |
| 323 return true; | 441 return true; |
| 324 } | 442 } |
| 325 | 443 |
| 326 /// Returns true iff the continuation consists of a continuation | 444 /// Returns true iff the continuation consists of a continuation |
| 327 /// invocation, passing on all parameters. Special cases exist (see below). | 445 /// invocation, passing on all parameters. Special cases exist (see below). |
| 328 bool _isEtaCont(Continuation cont) { | 446 bool _isEtaCont(Continuation cont) { |
| 329 if (cont.isReturnContinuation || cont.body is! InvokeContinuation) { | 447 if (_isRemoved(cont)) return false; |
| 448 |
| 449 if (!cont.isJoinContinuation || cont.body is! InvokeContinuation) { |
| 330 return false; | 450 return false; |
| 331 } | 451 } |
| 332 | 452 |
| 333 InvokeContinuation invoke = cont.body; | 453 InvokeContinuation invoke = cont.body; |
| 334 Continuation invokedCont = invoke.continuation.definition; | 454 Continuation invokedCont = invoke.continuation.definition; |
| 335 | 455 |
| 336 // Do not eta-reduce return join-points since the direct-style code is worse | 456 // Do not eta-reduce return join-points since the direct-style code is worse |
| 337 // in the common case (i.e. returns are moved inside `if` branches). | 457 // in the common case (i.e. returns are moved inside `if` branches). |
| 338 if (invokedCont.isReturnContinuation) { | 458 if (invokedCont.isReturnContinuation) { |
| 339 return false; | 459 return false; |
| 340 } | 460 } |
| 341 | 461 |
| 342 // Do not perform reductions replace a function call continuation with a | |
| 343 // non-call continuation. The invoked continuation is definitely not a call | |
| 344 // continuation, because it has a direct invocation in this continuation's | |
| 345 // body. | |
| 346 bool isCallContinuation(Continuation continuation) { | |
| 347 Reference<Continuation> current = cont.firstRef; | |
| 348 while (current != null) { | |
| 349 if (current.parent is InvokeContinuation) { | |
| 350 InvokeContinuation invoke = current.parent; | |
| 351 if (invoke.continuation.definition == continuation) return false; | |
| 352 } | |
| 353 current = current.next; | |
| 354 } | |
| 355 return true; | |
| 356 } | |
| 357 if (isCallContinuation(cont)) { | |
| 358 return false; | |
| 359 } | |
| 360 | |
| 361 // Translation to direct style generates different statements for recursive | 462 // Translation to direct style generates different statements for recursive |
| 362 // and non-recursive invokes. It should still be possible to apply eta-cont if | 463 // and non-recursive invokes. It should still be possible to apply eta-cont if |
| 363 // this is not a self-invocation. | 464 // this is not a self-invocation. |
| 364 // | 465 // |
| 365 // TODO(kmillikin): Remove this restriction if it makes sense to do so. | 466 // TODO(kmillikin): Remove this restriction if it makes sense to do so. |
| 366 if (invoke.isRecursive) { | 467 if (invoke.isRecursive) { |
| 367 return false; | 468 return false; |
| 368 } | 469 } |
| 369 | 470 |
| 370 // If cont has more parameters than the invocation has arguments, the extra | 471 // If cont has more parameters than the invocation has arguments, the extra |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 Expression _unfoldDeadRefinements(Expression node) { | 507 Expression _unfoldDeadRefinements(Expression node) { |
| 407 while (node is LetPrim) { | 508 while (node is LetPrim) { |
| 408 LetPrim let = node; | 509 LetPrim let = node; |
| 409 Primitive prim = let.primitive; | 510 Primitive prim = let.primitive; |
| 410 if (prim.hasAtLeastOneUse || prim is! Refinement) return node; | 511 if (prim.hasAtLeastOneUse || prim is! Refinement) return node; |
| 411 node = node.next; | 512 node = node.next; |
| 412 } | 513 } |
| 413 return node; | 514 return node; |
| 414 } | 515 } |
| 415 | 516 |
| 517 bool _isBranchRedex(Branch branch) { |
| 518 return _isUselessIf(branch) || branch.condition.definition is Constant; |
| 519 } |
| 520 |
| 416 bool _isBranchTargetOfUselessIf(Continuation cont) { | 521 bool _isBranchTargetOfUselessIf(Continuation cont) { |
| 417 // A useless-if has an empty then and else branch, e.g. `if (cond);`. | 522 // A useless-if has an empty then and else branch, e.g. `if (cond);`. |
| 418 // | 523 // |
| 419 // Detect T or F in | 524 // Detect T or F in |
| 420 // | 525 // |
| 421 // let cont Join() = ... | 526 // let cont Join() = ... |
| 422 // in let cont T() = Join() | 527 // in let cont T() = Join() |
| 423 // F() = Join() | 528 // F() = Join() |
| 424 // in branch condition T F | 529 // in branch condition T F |
| 425 // | 530 // |
| 426 if (!cont.hasExactlyOneUse) return false; | 531 if (!cont.hasExactlyOneUse) return false; |
| 427 if (cont.firstRef.parent is! Branch) return false; | 532 Node use = cont.firstRef.parent; |
| 428 Branch branch = cont.firstRef.parent; | 533 if (use is! Branch) return false; |
| 534 return _isUselessIf(use); |
| 535 } |
| 429 | 536 |
| 430 // Are both continuations the same InvokeContinuation on a join? | 537 bool _isUselessIf(Branch branch) { |
| 431 Continuation trueCont = branch.trueContinuation.definition; | 538 Continuation trueCont = branch.trueContinuation.definition; |
| 432 Expression trueBody = _unfoldDeadRefinements(trueCont.body); | 539 Expression trueBody = _unfoldDeadRefinements(trueCont.body); |
| 433 if (trueBody is! InvokeContinuation) return false; | 540 if (trueBody is! InvokeContinuation) return false; |
| 434 Continuation falseCont = branch.falseContinuation.definition; | 541 Continuation falseCont = branch.falseContinuation.definition; |
| 435 Expression falseBody = _unfoldDeadRefinements(falseCont.body); | 542 Expression falseBody = _unfoldDeadRefinements(falseCont.body); |
| 436 if (falseBody is! InvokeContinuation) return false; | 543 if (falseBody is! InvokeContinuation) return false; |
| 437 InvokeContinuation trueInvoke = trueBody; | 544 InvokeContinuation trueInvoke = trueBody; |
| 438 InvokeContinuation falseInvoke = falseBody; | 545 InvokeContinuation falseInvoke = falseBody; |
| 439 if (trueInvoke.continuation.definition != | 546 if (trueInvoke.continuation.definition != |
| 440 falseInvoke.continuation.definition) { | 547 falseInvoke.continuation.definition) { |
| 441 return false; | 548 return false; |
| 442 } | 549 } |
| 443 assert(trueInvoke.arguments.length == falseInvoke.arguments.length); | 550 assert(trueInvoke.arguments.length == falseInvoke.arguments.length); |
| 444 // Matching zero arguments should be adequate, since isomorphic true and false | 551 // Matching zero arguments should be adequate, since isomorphic true and false |
| 445 // invocations should result in redundant phis which are removed elsewhere. | 552 // invocations should result in redundant phis which are removed elsewhere. |
| 446 if (trueInvoke.arguments.isNotEmpty) return false; | 553 if (trueInvoke.arguments.isNotEmpty) return false; |
| 447 return true; | 554 return true; |
| 448 } | 555 } |
| 449 | 556 |
| 450 bool _isDeadParameter(Parameter parameter) { | 557 bool _isDeadParameter(Parameter parameter) { |
| 558 if (_isParameterRemoved(parameter)) return false; |
| 559 |
| 451 // We cannot remove function parameters as an intraprocedural optimization. | 560 // We cannot remove function parameters as an intraprocedural optimization. |
| 452 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { | 561 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { |
| 453 return false; | 562 return false; |
| 454 } | 563 } |
| 455 | 564 |
| 456 // We cannot remove exception handler parameters, they have a fixed arity | |
| 457 // of two. | |
| 458 if (parameter.parent.parent is LetHandler) { | |
| 459 return false; | |
| 460 } | |
| 461 | |
| 462 // We cannot remove the parameter to a call continuation, because the | 565 // We cannot remove the parameter to a call continuation, because the |
| 463 // resulting expression will not be well-formed (call continuations have | 566 // resulting expression will not be well-formed (call continuations have |
| 464 // exactly one argument). The return continuation is a call continuation, so | 567 // exactly one argument). The return continuation is a call continuation, so |
| 465 // we cannot remove its dummy parameter. | 568 // we cannot remove its dummy parameter. |
| 466 Continuation continuation = parameter.parent; | 569 Continuation continuation = parameter.parent; |
| 467 if (continuation.isReturnContinuation) return false; | 570 if (!continuation.isJoinContinuation) return false; |
| 468 Reference<Continuation> current = continuation.firstRef; | 571 |
| 469 while (current != null) { | |
| 470 if (current.parent is! InvokeContinuation) return false; | |
| 471 InvokeContinuation invoke = current.parent; | |
| 472 if (invoke.continuation.definition != continuation) return false; | |
| 473 current = current.next; | |
| 474 } | |
| 475 return true; | 572 return true; |
| 476 } | 573 } |
| 477 | 574 |
| 478 /// Traverses a term and adds any found redexes to the worklist. | 575 /// Traverses a term and adds any found redexes to the worklist. |
| 479 class _RedexVisitor extends TrampolineRecursiveVisitor { | 576 class _RedexVisitor extends TrampolineRecursiveVisitor { |
| 480 final List<_ReductionTask> worklist; | 577 final List<_ReductionTask> worklist; |
| 481 | 578 |
| 482 _RedexVisitor(this.worklist); | 579 _RedexVisitor(this.worklist); |
| 483 | 580 |
| 484 void processLetPrim(LetPrim node) { | 581 void processLetPrim(LetPrim node) { |
| 485 if (_isDeadVal(node)) { | 582 if (_isDeadVal(node)) { |
| 486 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); | 583 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); |
| 487 } | 584 } |
| 488 } | 585 } |
| 489 | 586 |
| 490 void processBranch(Branch node) { | 587 void processBranch(Branch node) { |
| 491 if (node.condition.definition is Constant) { | 588 if (_isBranchRedex(node)) { |
| 492 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, node)); | 589 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, node)); |
| 493 } | 590 } |
| 494 } | 591 } |
| 495 | 592 |
| 496 void processContinuation(Continuation node) { | 593 void processContinuation(Continuation node) { |
| 497 // While it would be nice to remove exception handlers that are provably | 594 // While it would be nice to remove exception handlers that are provably |
| 498 // unnecessary (e.g., the body cannot throw), that takes more sophisticated | 595 // unnecessary (e.g., the body cannot throw), that takes more sophisticated |
| 499 // analysis than we do in this pass. | 596 // analysis than we do in this pass. |
| 500 if (node.parent is LetHandler) return; | 597 if (node.parent is LetHandler) return; |
| 501 | 598 |
| 502 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex | 599 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex |
| 503 // is invoked exactly once. We prioritize continuation beta-redexes over | 600 // is invoked exactly once. We prioritize continuation beta-redexes over |
| 504 // eta-redexes because some reductions (e.g., dead parameter elimination) | 601 // eta-redexes because some reductions (e.g., dead parameter elimination) |
| 505 // can destroy a continuation eta-redex. If we prioritized eta- over | 602 // can destroy a continuation eta-redex. If we prioritized eta- over |
| 506 // beta-redexes, this would implicitly "create" the corresponding beta-redex | 603 // beta-redexes, this would implicitly "create" the corresponding beta-redex |
| 507 // (in the sense that it would still apply) and the algorithm would not | 604 // (in the sense that it would still apply) and the algorithm would not |
| 508 // detect it. | 605 // detect it. |
| 509 if (_isDeadCont(node)) { | 606 if (_isDeadCont(node)) { |
| 510 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); | 607 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
| 511 } else if (_isBetaContLin(node)){ | 608 } else if (_isBetaContLin(node)){ |
| 512 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | 609 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); |
| 513 } else if (_isEtaCont(node)) { | 610 } else if (_isEtaCont(node)) { |
| 514 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); | 611 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
| 515 } else if (_isBranchTargetOfUselessIf(node)) { | |
| 516 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, | |
| 517 node.firstRef.parent)); | |
| 518 } | 612 } |
| 519 } | 613 } |
| 520 | 614 |
| 521 void processParameter(Parameter node) { | 615 void processParameter(Parameter node) { |
| 522 if (_isDeadParameter(node)) { | 616 if (_isDeadParameter(node)) { |
| 523 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); | 617 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); |
| 524 } | 618 } |
| 525 } | 619 } |
| 526 } | 620 } |
| 527 | 621 |
| 528 /// Traverses a deleted CPS term, marking nodes that might participate in a | 622 /// Traverses a deleted CPS term, marking nodes that might participate in a |
| 529 /// redex as deleted and adding newly created redexes to the worklist. | 623 /// redex as deleted and adding newly created redexes to the worklist. |
| 530 /// | 624 /// |
| 531 /// Deleted nodes that might participate in a reduction task are marked so that | 625 /// Deleted nodes that might participate in a reduction task are marked so that |
| 532 /// any corresponding tasks can be skipped. Nodes are marked so by setting | 626 /// any corresponding tasks can be skipped. Nodes are marked so by setting |
| 533 /// their parent to the deleted sentinel. | 627 /// their parent to the deleted sentinel. |
| 534 class _RemovalVisitor extends TrampolineRecursiveVisitor { | 628 class _RemovalVisitor extends TrampolineRecursiveVisitor { |
| 535 final List<_ReductionTask> worklist; | 629 final List<_ReductionTask> worklist; |
| 536 | 630 |
| 537 _RemovalVisitor(this.worklist); | 631 _RemovalVisitor(this.worklist); |
| 538 | 632 |
| 539 void processLetPrim(LetPrim node) { | 633 void processLetPrim(LetPrim node) { |
| 540 node.parent = ShrinkingReducer._DELETED; | 634 node.parent = null; |
| 541 } | 635 } |
| 542 | 636 |
| 543 void processContinuation(Continuation node) { | 637 void processContinuation(Continuation node) { |
| 544 node.parent = ShrinkingReducer._DELETED; | 638 node.parent = null; |
| 545 } | 639 } |
| 546 | 640 |
| 547 void processReference(Reference reference) { | 641 void processReference(Reference reference) { |
| 548 reference.unlink(); | 642 reference.unlink(); |
| 549 | 643 |
| 550 if (reference.definition is Primitive) { | 644 if (reference.definition is Primitive) { |
| 551 Primitive primitive = reference.definition; | 645 Primitive primitive = reference.definition.unrefined; |
| 552 Node parent = primitive.parent; | 646 Node parent = primitive.parent; |
| 553 // The parent might be the deleted sentinel, or it might be a | 647 // The parent might be the deleted sentinel, or it might be a |
| 554 // Continuation or FunctionDefinition if the primitive is an argument. | 648 // Continuation or FunctionDefinition if the primitive is an argument. |
| 555 if (parent is LetPrim && _isDeadVal(parent)) { | 649 if (parent is LetPrim && _isDeadVal(parent)) { |
| 556 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 650 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 651 } else if (primitive is Parameter && _isDeadParameter(primitive)) { |
| 652 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, |
| 653 primitive)); |
| 557 } | 654 } |
| 558 } else if (reference.definition is Continuation) { | 655 } else if (reference.definition is Continuation) { |
| 559 Continuation cont = reference.definition; | 656 Continuation cont = reference.definition; |
| 560 Node parent = cont.parent; | 657 Node parent = cont.parent; |
| 561 // The parent might be the deleted sentinel, or it might be a | 658 // The parent might be the deleted sentinel, or it might be a |
| 562 // Body if the continuation is the return continuation. | 659 // Body if the continuation is the return continuation. |
| 563 if (parent is LetCont) { | 660 if (parent is LetCont) { |
| 564 if (cont.isRecursive && cont.hasAtMostOneUse) { | 661 if (cont.isRecursive && cont.hasAtMostOneUse) { |
| 565 // Convert recursive to nonrecursive continuations. If the | 662 // Convert recursive to nonrecursive continuations. If the |
| 566 // continuation is still in use, it is either dead and will be | 663 // continuation is still in use, it is either dead and will be |
| 567 // removed, or it is called nonrecursively outside its body. | 664 // removed, or it is called nonrecursively outside its body. |
| 568 cont.isRecursive = false; | 665 cont.isRecursive = false; |
| 569 } | 666 } |
| 570 if (_isDeadCont(cont)) { | 667 if (_isDeadCont(cont)) { |
| 571 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); | 668 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); |
| 572 } else if (_isBetaContLin(cont)) { | 669 } else if (_isBetaContLin(cont)) { |
| 573 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); | 670 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); |
| 574 } else if (_isBranchTargetOfUselessIf(cont)) { | 671 } else if (_isBranchTargetOfUselessIf(cont)) { |
| 575 worklist.add( | 672 worklist.add( |
| 576 new _ReductionTask(_ReductionKind.BRANCH, cont.firstRef.parent)); | 673 new _ReductionTask(_ReductionKind.BRANCH, cont.firstRef.parent)); |
| 577 } | 674 } |
| 578 } | 675 } |
| 579 } | 676 } |
| 580 } | 677 } |
| 581 } | 678 } |
| 582 | 679 |
| 583 | 680 enum _ReductionKind { |
| 584 | 681 DEAD_VAL, |
| 585 class _ReductionKind { | 682 DEAD_CONT, |
| 586 final String name; | 683 BETA_CONT_LIN, |
| 587 final int hashCode; | 684 ETA_CONT, |
| 588 | 685 DEAD_PARAMETER, |
| 589 const _ReductionKind(this.name, this.hashCode); | 686 BRANCH |
| 590 | |
| 591 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); | |
| 592 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); | |
| 593 static const _ReductionKind BETA_CONT_LIN = | |
| 594 const _ReductionKind('beta-cont-lin', 2); | |
| 595 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); | |
| 596 static const _ReductionKind DEAD_PARAMETER = | |
| 597 const _ReductionKind('dead-parameter', 4); | |
| 598 static const _ReductionKind BRANCH = const _ReductionKind('branch', 5); | |
| 599 | |
| 600 String toString() => name; | |
| 601 } | 687 } |
| 602 | 688 |
| 603 /// Represents a reduction task on the worklist. Implements both hashCode and | 689 /// Represents a reduction task on the worklist. Implements both hashCode and |
| 604 /// operator== since instantiations are used as Set elements. | 690 /// operator== since instantiations are used as Set elements. |
| 605 class _ReductionTask { | 691 class _ReductionTask { |
| 606 final _ReductionKind kind; | 692 final _ReductionKind kind; |
| 607 final Node node; | 693 final Node node; |
| 608 | 694 |
| 609 int get hashCode { | 695 int get hashCode { |
| 610 assert(kind.hashCode < (1 << 3)); | 696 return (node.hashCode << 3) | kind.index; |
| 611 return (node.hashCode << 3) | kind.hashCode; | |
| 612 } | 697 } |
| 613 | 698 |
| 614 _ReductionTask(this.kind, this.node) { | 699 _ReductionTask(this.kind, this.node) { |
| 615 assert(node is Continuation || node is LetPrim || node is Parameter || | 700 assert(node is Continuation || node is LetPrim || node is Parameter || |
| 616 node is Branch); | 701 node is Branch); |
| 617 } | 702 } |
| 618 | 703 |
| 619 bool operator==(_ReductionTask that) { | 704 bool operator==(_ReductionTask that) { |
| 620 return (that.kind == this.kind && that.node == this.node); | 705 return (that.kind == this.kind && that.node == this.node); |
| 621 } | 706 } |
| 622 | 707 |
| 623 String toString() => "$kind: $node"; | 708 String toString() => "$kind: $node"; |
| 624 } | 709 } |
| 625 | |
| 626 /// A dummy class used solely to mark nodes as deleted once they are removed | |
| 627 /// from a term. | |
| 628 class _DeletedNode extends Node { | |
| 629 accept(_) {} | |
| 630 setParentPointers() {} | |
| 631 } | |
| OLD | NEW |