| 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 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 break; | 76 break; |
| 77 case _ReductionKind.DEAD_CONT: | 77 case _ReductionKind.DEAD_CONT: |
| 78 _reduceDeadCont(task); | 78 _reduceDeadCont(task); |
| 79 break; | 79 break; |
| 80 case _ReductionKind.BETA_CONT_LIN: | 80 case _ReductionKind.BETA_CONT_LIN: |
| 81 _reduceBetaContLin(task); | 81 _reduceBetaContLin(task); |
| 82 break; | 82 break; |
| 83 case _ReductionKind.ETA_CONT: | 83 case _ReductionKind.ETA_CONT: |
| 84 _reduceEtaCont(task); | 84 _reduceEtaCont(task); |
| 85 break; | 85 break; |
| 86 case _ReductionKind.DEAD_PARAMETER: |
| 87 _reduceDeadParameter(task); |
| 88 break; |
| 86 default: | 89 default: |
| 87 assert(false); | 90 assert(false); |
| 88 } | 91 } |
| 89 } | 92 } |
| 90 | 93 |
| 91 /// Applies the dead-val reduction: | 94 /// Applies the dead-val reduction: |
| 92 /// letprim x = V in E -> E (x not free in E). | 95 /// letprim x = V in E -> E (x not free in E). |
| 93 void _reduceDeadVal(_ReductionTask task) { | 96 void _reduceDeadVal(_ReductionTask task) { |
| 94 assert(_isDeadVal(task.node)); | 97 assert(_isDeadVal(task.node)); |
| 95 | 98 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 | 172 |
| 170 InvokeContinuation invoke = cont.body; | 173 InvokeContinuation invoke = cont.body; |
| 171 Continuation wrappedCont = invoke.continuation.definition; | 174 Continuation wrappedCont = invoke.continuation.definition; |
| 172 | 175 |
| 173 // Replace all occurrences with the wrapped continuation. | 176 // Replace all occurrences with the wrapped continuation. |
| 174 wrappedCont.substituteFor(cont); | 177 wrappedCont.substituteFor(cont); |
| 175 | 178 |
| 176 // Perform bookkeeping on removed body and scan for new redexes. | 179 // Perform bookkeeping on removed body and scan for new redexes. |
| 177 new _RemovalVisitor(_worklist).visit(cont); | 180 new _RemovalVisitor(_worklist).visit(cont); |
| 178 } | 181 } |
| 182 |
| 183 void _reduceDeadParameter(_ReductionTask task) { |
| 184 assert(_isDeadParameter(task.node)); |
| 185 |
| 186 Parameter parameter = task.node; |
| 187 Continuation continuation = parameter.parent; |
| 188 int index = parameter.parent_index; |
| 189 |
| 190 // Remove the index'th argument from each invocation. |
| 191 Reference<Continuation> current = continuation.firstRef; |
| 192 while (current != null) { |
| 193 InvokeContinuation invoke = current.parent; |
| 194 Reference<Primitive> argument = invoke.arguments[index]; |
| 195 argument.unlink(); |
| 196 // Removing an argument can create a dead parameter or dead value redex. |
| 197 if (argument.definition is Parameter) { |
| 198 if (_isDeadParameter(argument.definition)) { |
| 199 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, |
| 200 argument.definition)); |
| 201 } |
| 202 } else { |
| 203 Node parent = argument.definition.parent; |
| 204 if (parent is LetPrim) { |
| 205 if (_isDeadVal(parent)) { |
| 206 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 207 } |
| 208 } |
| 209 } |
| 210 invoke.arguments.removeAt(index); |
| 211 current = current.next; |
| 212 } |
| 213 // Copy the parameters above index down. |
| 214 List<Parameter> parameters = continuation.parameters; |
| 215 for (int i = index; i < parameters.length - 1; ++i) { |
| 216 Parameter p = parameters[i + 1]; |
| 217 parameters[i] = p; |
| 218 p.parent_index = i; |
| 219 } |
| 220 parameters.removeLast(); |
| 221 |
| 222 // Removing an unused parameter can create an eta-redex. |
| 223 if (_isEtaCont(continuation)) { |
| 224 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); |
| 225 } |
| 226 } |
| 179 } | 227 } |
| 180 | 228 |
| 181 /// Returns true iff the bound primitive is unused. | 229 /// Returns true iff the bound primitive is unused. |
| 182 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; | 230 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; |
| 183 | 231 |
| 184 /// Returns true iff the continuation is unused. | 232 /// Returns true iff the continuation is unused. |
| 185 bool _isDeadCont(Continuation cont) { | 233 bool _isDeadCont(Continuation cont) { |
| 186 assert(!cont.isReturnContinuation); | 234 return !cont.isReturnContinuation && !cont.hasAtLeastOneUse; |
| 187 return !cont.hasAtLeastOneUse; | |
| 188 } | 235 } |
| 189 | 236 |
| 190 /// Returns true iff the continuation is used exactly once, and that | 237 /// Returns true iff the continuation has a body (i.e., it is not the return |
| 191 /// use is as the continuation of a continuation invocation. | 238 /// continuation), it is used exactly once, and that use is as the continuation |
| 239 /// of a continuation invocation. |
| 192 bool _isBetaContLin(Continuation cont) { | 240 bool _isBetaContLin(Continuation cont) { |
| 193 if (!cont.hasExactlyOneUse) { | 241 // There is a restriction on continuation eta-redexes that the body is not an |
| 242 // invocation of the return continuation, because that leads to worse code |
| 243 // when translating back to direct style (it duplicates returns). There is no |
| 244 // such restriction here because continuation beta-reduction is only performed |
| 245 // for singly referenced continuations. Thus, there is no possibility of code |
| 246 // duplication. |
| 247 if (cont.isReturnContinuation || !cont.hasExactlyOneUse) { |
| 194 return false; | 248 return false; |
| 195 } | 249 } |
| 196 | 250 |
| 197 if (cont.firstRef.parent is InvokeContinuation) { | 251 if (cont.firstRef.parent is InvokeContinuation) { |
| 198 InvokeContinuation invoke = cont.firstRef.parent; | 252 InvokeContinuation invoke = cont.firstRef.parent; |
| 199 return (cont == invoke.continuation.definition); | 253 return (cont == invoke.continuation.definition); |
| 200 } | 254 } |
| 201 | 255 |
| 202 return false; | 256 return false; |
| 203 } | 257 } |
| 204 | 258 |
| 205 /// Returns true iff the continuation consists of a continuation | 259 /// Returns true iff the continuation consists of a continuation |
| 206 /// invocation, passing on all parameters. Special cases exist (see below). | 260 /// invocation, passing on all parameters. Special cases exist (see below). |
| 207 bool _isEtaCont(Continuation cont) { | 261 bool _isEtaCont(Continuation cont) { |
| 208 if (cont.body is! InvokeContinuation) { | 262 if (cont.isReturnContinuation || cont.body is! InvokeContinuation) { |
| 209 return false; | 263 return false; |
| 210 } | 264 } |
| 211 | 265 |
| 212 InvokeContinuation invoke = cont.body; | 266 InvokeContinuation invoke = cont.body; |
| 213 Continuation invokedCont = invoke.continuation.definition; | 267 Continuation invokedCont = invoke.continuation.definition; |
| 214 | 268 |
| 215 // Do not eta-reduce return join-points since the resulting code is worse | 269 // Do not eta-reduce return join-points since the direct-style code is worse |
| 216 // in the common case (i.e. returns are moved inside `if` branches). | 270 // in the common case (i.e. returns are moved inside `if` branches). |
| 217 if (invokedCont.isReturnContinuation) { | 271 if (invokedCont.isReturnContinuation) { |
| 218 return false; | 272 return false; |
| 219 } | 273 } |
| 220 | 274 |
| 221 // Translation to direct style generates different statements for recursive | 275 // Translation to direct style generates different statements for recursive |
| 222 // and non-recursive invokes. It should be possible to apply eta-cont, but | 276 // and non-recursive invokes. It should still be possible to apply eta-cont if |
| 223 // higher order continuations require escape analysis, left as a possibility | 277 // this is not a self-invocation. |
| 224 // for future improvements. | 278 // |
| 279 // TODO(kmillikin): Remove this restriction if it makes sense to do so. |
| 225 if (invoke.isRecursive) { | 280 if (invoke.isRecursive) { |
| 226 return false; | 281 return false; |
| 227 } | 282 } |
| 228 | 283 |
| 284 // If cont has more parameters than the invocation has arguments, the extra |
| 285 // parameters will be dead and dead-parameter will eventually create the |
| 286 // eta-redex if possible. |
| 287 // |
| 288 // If the invocation's arguments are simply a permutation of cont's |
| 289 // parameters, then there is likewise a possible reduction that involves |
| 290 // rewriting the invocations of cont. We are missing that reduction here. |
| 291 // |
| 292 // If cont has fewer parameters than the invocation has arguments then a |
| 293 // reduction would still possible, since the extra invocation arguments must |
| 294 // be in scope at all the invocations of cont. For example: |
| 295 // |
| 296 // let cont k1(x1) = k0(x0, x1) in E -eta-> E' |
| 297 // where E' has k0(x0, v) substituted for each k1(v). |
| 298 // |
| 299 // HOWEVER, adding continuation parameters is unlikely to be an optimization |
| 300 // since it duplicates assignments used in direct-style to implement parameter |
| 301 // passing. |
| 302 // |
| 303 // TODO(kmillikin): find real occurrences of these patterns, and see if they |
| 304 // can be optimized. |
| 229 if (cont.parameters.length != invoke.arguments.length) { | 305 if (cont.parameters.length != invoke.arguments.length) { |
| 230 return false; | 306 return false; |
| 231 } | 307 } |
| 232 | 308 |
| 233 // TODO(jgruber): Linear in the parameter count. Can be improved to near | 309 // TODO(jgruber): Linear in the parameter count. Can be improved to near |
| 234 // constant time by using union-find data structure. | 310 // constant time by using union-find data structure. |
| 235 for (int i = 0; i < cont.parameters.length; i++) { | 311 for (int i = 0; i < cont.parameters.length; i++) { |
| 236 if (invoke.arguments[i].definition != cont.parameters[i]) { | 312 if (invoke.arguments[i].definition != cont.parameters[i]) { |
| 237 return false; | 313 return false; |
| 238 } | 314 } |
| 239 } | 315 } |
| 240 | 316 |
| 241 return true; | 317 return true; |
| 242 } | 318 } |
| 243 | 319 |
| 320 bool _isDeadParameter(Parameter parameter) { |
| 321 // We cannot remove function parameters as an intraprocedural optimization. |
| 322 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { |
| 323 return false; |
| 324 } |
| 325 |
| 326 // We cannot remove the parameter to a call continuation, because the |
| 327 // resulting expression will not be well-formed (call continuations have |
| 328 // exactly one argument). The return continuation is a call continuation, so |
| 329 // we cannot remove its dummy parameter. |
| 330 Continuation continuation = parameter.parent; |
| 331 if (continuation.isReturnContinuation) return false; |
| 332 Reference<Continuation> current = continuation.firstRef; |
| 333 while (current != null) { |
| 334 if (current.parent is! InvokeContinuation) return false; |
| 335 InvokeContinuation invoke = current.parent; |
| 336 if (invoke.continuation.definition != continuation) return false; |
| 337 current = current.next; |
| 338 } |
| 339 return true; |
| 340 } |
| 341 |
| 244 /// Traverses a term and adds any found redexes to the worklist. | 342 /// Traverses a term and adds any found redexes to the worklist. |
| 245 class _RedexVisitor extends RecursiveVisitor { | 343 class _RedexVisitor extends RecursiveVisitor { |
| 246 final Set<_ReductionTask> worklist; | 344 final Set<_ReductionTask> worklist; |
| 247 | 345 |
| 248 _RedexVisitor(this.worklist); | 346 _RedexVisitor(this.worklist); |
| 249 | 347 |
| 250 void processLetPrim(LetPrim node) { | 348 void processLetPrim(LetPrim node) { |
| 251 if (_isDeadVal(node)) { | 349 if (_isDeadVal(node)) { |
| 252 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); | 350 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); |
| 253 } | 351 } |
| 254 } | 352 } |
| 255 | 353 |
| 256 void processContinuation(Continuation node) { | 354 void processContinuation(Continuation node) { |
| 355 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex |
| 356 // is invoked exactly once. We prioritize continuation beta-redexes over |
| 357 // eta-redexes because some reductions (e.g., dead parameter elimination) |
| 358 // can destroy a continuation eta-redex. If we prioritized eta- over |
| 359 // beta-redexes, this would implicitly "create" the corresponding beta-redex |
| 360 // (in the sense that it would still apply) and the algorithm would not |
| 361 // detect it. |
| 257 if (_isDeadCont(node)) { | 362 if (_isDeadCont(node)) { |
| 258 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); | 363 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
| 364 } else if (_isBetaContLin(node)){ |
| 365 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); |
| 259 } else if (_isEtaCont(node)) { | 366 } else if (_isEtaCont(node)) { |
| 260 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); | 367 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
| 261 } else if (_isBetaContLin(node)){ | 368 } |
| 262 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | 369 } |
| 370 |
| 371 void processParameter(Parameter node) { |
| 372 if (_isDeadParameter(node)) { |
| 373 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); |
| 263 } | 374 } |
| 264 } | 375 } |
| 265 } | 376 } |
| 266 | 377 |
| 267 /// Traverses a deleted CPS term, marking nodes that might participate in a | 378 /// Traverses a deleted CPS term, marking nodes that might participate in a |
| 268 /// redex as deleted and adding newly created redexes to the worklist. | 379 /// redex as deleted and adding newly created redexes to the worklist. |
| 269 /// | 380 /// |
| 270 /// Deleted nodes that might participate in a reduction task are marked so that | 381 /// Deleted nodes that might participate in a reduction task are marked so that |
| 271 /// any corresponding tasks can be skipped. Nodes are marked so by setting | 382 /// any corresponding tasks can be skipped. Nodes are marked so by setting |
| 272 /// their parent to the deleted sentinel. | 383 /// their parent to the deleted sentinel. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 291 Node parent = primitive.parent; | 402 Node parent = primitive.parent; |
| 292 // The parent might be the deleted sentinel, or it might be a | 403 // The parent might be the deleted sentinel, or it might be a |
| 293 // Continuation or FunctionDefinition if the primitive is an argument. | 404 // Continuation or FunctionDefinition if the primitive is an argument. |
| 294 if (parent is LetPrim && _isDeadVal(parent)) { | 405 if (parent is LetPrim && _isDeadVal(parent)) { |
| 295 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 406 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 296 } | 407 } |
| 297 } else if (reference.definition is Continuation) { | 408 } else if (reference.definition is Continuation) { |
| 298 Continuation cont = reference.definition; | 409 Continuation cont = reference.definition; |
| 299 Node parent = cont.parent; | 410 Node parent = cont.parent; |
| 300 // The parent might be the deleted sentinel, or it might be a | 411 // The parent might be the deleted sentinel, or it might be a |
| 301 // FunctionDefinition if the continuation is the return continuation. | 412 // RunnableBody if the continuation is the return continuation. |
| 302 if (parent is LetCont) { | 413 if (parent is LetCont) { |
| 303 if (cont.isRecursive && cont.hasAtMostOneUse) { | 414 if (cont.isRecursive && cont.hasAtMostOneUse) { |
| 304 // Convert recursive to nonrecursive continuations. If the | 415 // Convert recursive to nonrecursive continuations. If the |
| 305 // continuation is still in use, it is either dead and will be | 416 // continuation is still in use, it is either dead and will be |
| 306 // removed, or it is called nonrecursively outside its body. | 417 // removed, or it is called nonrecursively outside its body. |
| 307 cont.isRecursive = false; | 418 cont.isRecursive = false; |
| 308 } | 419 } |
| 309 if (_isDeadCont(cont)) { | 420 if (_isDeadCont(cont)) { |
| 310 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); | 421 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); |
| 422 } else if (_isBetaContLin(cont)) { |
| 423 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); |
| 311 } | 424 } |
| 312 } | 425 } |
| 313 } | 426 } |
| 314 } | 427 } |
| 315 } | 428 } |
| 316 | 429 |
| 317 /// Traverses the CPS term and sets node.parent for each visited node. | 430 /// Traverses the CPS term and sets node.parent for each visited node. |
| 318 class ParentVisitor extends RecursiveVisitor { | 431 class ParentVisitor extends RecursiveVisitor { |
| 319 processFunctionDefinition(FunctionDefinition node) { | 432 processFunctionDefinition(FunctionDefinition node) { |
| 320 node.body.parent = node; | 433 node.body.parent = node; |
| 321 node.parameters.forEach((Definition p) => p.parent = node); | 434 int index = 0; |
| 435 node.parameters.forEach((Parameter parameter) { |
| 436 parameter.parent = node; |
| 437 parameter.parent_index = index++; |
| 438 }); |
| 322 } | 439 } |
| 323 | 440 |
| 324 processRunnableBody(RunnableBody node) { | 441 processRunnableBody(RunnableBody node) { |
| 442 node.returnContinuation.parent = node; |
| 325 node.body.parent = node; | 443 node.body.parent = node; |
| 326 } | 444 } |
| 327 | 445 |
| 328 processConstructorDefinition(ConstructorDefinition node) { | 446 processConstructorDefinition(ConstructorDefinition node) { |
| 329 node.body.parent = node; | 447 node.body.parent = node; |
| 330 node.parameters.forEach((Definition p) => p.parent = node); | 448 int index = 0; |
| 449 node.parameters.forEach((Parameter parameter) { |
| 450 parameter.parent = node; |
| 451 parameter.parent_index = index++; |
| 452 }); |
| 331 node.initializers.forEach((Initializer i) => i.parent = node); | 453 node.initializers.forEach((Initializer i) => i.parent = node); |
| 332 } | 454 } |
| 333 | 455 |
| 334 // Expressions. | 456 // Expressions. |
| 335 | 457 |
| 336 processFieldInitializer(FieldInitializer node) { | 458 processFieldInitializer(FieldInitializer node) { |
| 337 node.body.body.parent = node; | 459 node.body.body.parent = node; |
| 338 } | 460 } |
| 339 | 461 |
| 340 processSuperInitializer(SuperInitializer node) { | 462 processSuperInitializer(SuperInitializer node) { |
| 341 node.arguments.forEach( | 463 node.arguments.forEach( |
| 342 (RunnableBody argument) => argument.body.parent = node); | 464 (RunnableBody argument) => argument.body.parent = node); |
| 343 } | 465 } |
| 344 | 466 |
| 345 processLetPrim(LetPrim node) { | 467 processLetPrim(LetPrim node) { |
| 346 node.primitive.parent = node; | 468 node.primitive.parent = node; |
| 347 node.body.parent = node; | 469 node.body.parent = node; |
| 348 } | 470 } |
| 349 | 471 |
| 350 processLetCont(LetCont node) { | 472 processLetCont(LetCont node) { |
| 351 for (int i = 0; i < node.continuations.length; ++i) { | 473 int index = 0; |
| 352 Continuation cont = node.continuations[i]; | 474 node.continuations.forEach((Continuation continuation) { |
| 353 cont.parent = node; | 475 continuation.parent = node; |
| 354 cont.parent_index = i; | 476 continuation.parent_index = index++; |
| 355 } | 477 }); |
| 356 node.body.parent = node; | 478 node.body.parent = node; |
| 357 } | 479 } |
| 358 | 480 |
| 359 processInvokeStatic(InvokeStatic node) { | 481 processInvokeStatic(InvokeStatic node) { |
| 360 node.arguments.forEach((Reference ref) => ref.parent = node); | 482 node.arguments.forEach((Reference ref) => ref.parent = node); |
| 361 node.continuation.parent = node; | 483 node.continuation.parent = node; |
| 362 } | 484 } |
| 363 | 485 |
| 364 processInvokeContinuation(InvokeContinuation node) { | 486 processInvokeContinuation(InvokeContinuation node) { |
| 365 node.continuation.parent = node; | 487 node.continuation.parent = node; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 entry.key.parent = node; | 542 entry.key.parent = node; |
| 421 entry.value.parent = node; | 543 entry.value.parent = node; |
| 422 }); | 544 }); |
| 423 } | 545 } |
| 424 | 546 |
| 425 processCreateFunction(CreateFunction node) { | 547 processCreateFunction(CreateFunction node) { |
| 426 node.definition.parent = node; | 548 node.definition.parent = node; |
| 427 } | 549 } |
| 428 | 550 |
| 429 processContinuation(Continuation node) { | 551 processContinuation(Continuation node) { |
| 430 node.body.parent = node; | 552 if (node.body != null) node.body.parent = node; |
| 431 node.parameters.forEach((Parameter param) => param.parent = node); | 553 int index = 0; |
| 554 node.parameters.forEach((Parameter parameter) { |
| 555 parameter.parent = node; |
| 556 parameter.parent_index = index++; |
| 557 }); |
| 432 } | 558 } |
| 433 | 559 |
| 434 // Conditions. | 560 // Conditions. |
| 435 | 561 |
| 436 processIsTrue(IsTrue node) { | 562 processIsTrue(IsTrue node) { |
| 437 node.value.parent = node; | 563 node.value.parent = node; |
| 438 } | 564 } |
| 439 | 565 |
| 440 // JavaScript specific nodes. | 566 // JavaScript specific nodes. |
| 441 | 567 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 470 final String name; | 596 final String name; |
| 471 final int hashCode; | 597 final int hashCode; |
| 472 | 598 |
| 473 const _ReductionKind(this.name, this.hashCode); | 599 const _ReductionKind(this.name, this.hashCode); |
| 474 | 600 |
| 475 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); | 601 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); |
| 476 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); | 602 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); |
| 477 static const _ReductionKind BETA_CONT_LIN = | 603 static const _ReductionKind BETA_CONT_LIN = |
| 478 const _ReductionKind('beta-cont-lin', 2); | 604 const _ReductionKind('beta-cont-lin', 2); |
| 479 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); | 605 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); |
| 606 static const _ReductionKind DEAD_PARAMETER = |
| 607 const _ReductionKind('dead-parameter', 4); |
| 480 | 608 |
| 481 String toString() => name; | 609 String toString() => name; |
| 482 } | 610 } |
| 483 | 611 |
| 484 /// Represents a reduction task on the worklist. Implements both hashCode and | 612 /// Represents a reduction task on the worklist. Implements both hashCode and |
| 485 /// operator== since instantiations are used as Set elements. | 613 /// operator== since instantiations are used as Set elements. |
| 486 class _ReductionTask { | 614 class _ReductionTask { |
| 487 final _ReductionKind kind; | 615 final _ReductionKind kind; |
| 488 final Node node; | 616 final Node node; |
| 489 | 617 |
| 490 int get hashCode { | 618 int get hashCode { |
| 491 assert(kind.hashCode < (1 << 2)); | 619 assert(kind.hashCode < (1 << 3)); |
| 492 return (node.hashCode << 2) | kind.hashCode; | 620 return (node.hashCode << 3) | kind.hashCode; |
| 493 } | 621 } |
| 494 | 622 |
| 495 _ReductionTask(this.kind, this.node) { | 623 _ReductionTask(this.kind, this.node) { |
| 496 assert(node is Continuation || node is LetPrim); | 624 assert(node is Continuation || node is LetPrim || node is Parameter); |
| 497 } | 625 } |
| 498 | 626 |
| 499 bool operator==(_ReductionTask that) { | 627 bool operator==(_ReductionTask that) { |
| 500 return (that.kind == this.kind && that.node == this.node); | 628 return (that.kind == this.kind && that.node == this.node); |
| 501 } | 629 } |
| 502 | 630 |
| 503 String toString() => "$kind: $node"; | 631 String toString() => "$kind: $node"; |
| 504 } | 632 } |
| 505 | 633 |
| 506 /// A dummy class used solely to mark nodes as deleted once they are removed | 634 /// A dummy class used solely to mark nodes as deleted once they are removed |
| 507 /// from a term. | 635 /// from a term. |
| 508 class _DeletedNode extends Node { | 636 class _DeletedNode extends Node { |
| 509 accept(_) => null; | 637 accept(_) => null; |
| 510 } | 638 } |
| OLD | NEW |