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