Chromium Code Reviews| 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 be possible to apply eta-cont if this |
| 223 // higher order continuations require escape analysis, left as a possibility | 277 // is not a self-invocation, but higher order continuations require escape |
| 224 // for future improvements. | 278 // analysis, left as a possibility for future improvements. |
|
asgerf
2015/01/22 16:02:08
Update comment regarding higher order continuation
Kevin Millikin (Google)
2015/01/22 16:16:29
Yeah, I'll just get ride of that part.
| |
| 225 if (invoke.isRecursive) { | 279 if (invoke.isRecursive) { |
| 226 return false; | 280 return false; |
| 227 } | 281 } |
| 228 | 282 |
| 283 // If cont has more parameters than the invocation has arguments, the extra | |
| 284 // parameters will be dead and dead-parameter will eventually create the | |
| 285 // eta-redex if possible. | |
| 286 // | |
| 287 // If cont has fewer parameters than the invocation has arguments then a | |
| 288 // reduction is still possible, since the extra invocation arguments must be | |
| 289 // in scope at all the invocations of cont. For example: | |
| 290 // | |
| 291 // let cont k1(x1) = k0(x0, x1) in E -eta-> E' | |
| 292 // where E' has k0(x0, v) substituted for each k1(v). | |
| 293 // | |
| 294 // If the invocation's arguments are simply a permutation of cont's | |
| 295 // parameters, then there is likewise still a possible reduction that involves | |
| 296 // rewriting the invocations of cont. | |
| 297 // | |
| 298 // TODO(kmillikin): find real occurrences of these patterns, and if they | |
| 299 // exist, implement the reductions. | |
|
asgerf
2015/01/22 16:02:08
Can such code trigger other reductions? Otherwise
Kevin Millikin (Google)
2015/01/22 16:16:29
No, I don't think it enables other reductions ---
| |
| 229 if (cont.parameters.length != invoke.arguments.length) { | 300 if (cont.parameters.length != invoke.arguments.length) { |
| 230 return false; | 301 return false; |
| 231 } | 302 } |
| 232 | 303 |
| 233 // TODO(jgruber): Linear in the parameter count. Can be improved to near | 304 // TODO(jgruber): Linear in the parameter count. Can be improved to near |
| 234 // constant time by using union-find data structure. | 305 // constant time by using union-find data structure. |
| 235 for (int i = 0; i < cont.parameters.length; i++) { | 306 for (int i = 0; i < cont.parameters.length; i++) { |
| 236 if (invoke.arguments[i].definition != cont.parameters[i]) { | 307 if (invoke.arguments[i].definition != cont.parameters[i]) { |
| 237 return false; | 308 return false; |
| 238 } | 309 } |
| 239 } | 310 } |
| 240 | 311 |
| 241 return true; | 312 return true; |
| 242 } | 313 } |
| 243 | 314 |
| 315 bool _isDeadParameter(Parameter parameter) { | |
| 316 // We cannot remove function parameters as an intraprocedural optimization. | |
| 317 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { | |
| 318 return false; | |
| 319 } | |
| 320 | |
| 321 // We cannot remove the parameter to a call continuation, because the | |
| 322 // resulting expression will not be well-formed (call continuations have | |
| 323 // exactly one argument). The return continuation is a call continuation, so | |
| 324 // we cannot remove its dummy parameter. | |
| 325 Continuation continuation = parameter.parent; | |
| 326 if (continuation.isReturnContinuation) return false; | |
| 327 Reference<Continuation> current = continuation.firstRef; | |
| 328 while (current != null) { | |
| 329 if (current.parent is! InvokeContinuation) return false; | |
| 330 InvokeContinuation invoke = current.parent; | |
| 331 if (invoke.continuation.definition != continuation) return false; | |
| 332 current = current.next; | |
| 333 } | |
| 334 return true; | |
| 335 } | |
| 336 | |
| 244 /// Traverses a term and adds any found redexes to the worklist. | 337 /// Traverses a term and adds any found redexes to the worklist. |
| 245 class _RedexVisitor extends RecursiveVisitor { | 338 class _RedexVisitor extends RecursiveVisitor { |
| 246 final Set<_ReductionTask> worklist; | 339 final Set<_ReductionTask> worklist; |
| 247 | 340 |
| 248 _RedexVisitor(this.worklist); | 341 _RedexVisitor(this.worklist); |
| 249 | 342 |
| 250 void processLetPrim(LetPrim node) { | 343 void processLetPrim(LetPrim node) { |
| 251 if (_isDeadVal(node)) { | 344 if (_isDeadVal(node)) { |
| 252 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); | 345 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); |
| 253 } | 346 } |
| 254 } | 347 } |
| 255 | 348 |
| 256 void processContinuation(Continuation node) { | 349 void processContinuation(Continuation node) { |
| 350 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex | |
| 351 // is invoked exactly once. We prioritize continuation beta-redexes over | |
| 352 // eta-redexes because some reductions (e.g., dead parameter elimination) | |
| 353 // can destroy a continuation eta-redex. If we prioritized eta- over | |
| 354 // beta-redexes, this would implicitly "create" the corresponding beta-redex | |
| 355 // (in the sense that it would still apply) and the algorithm would not | |
| 356 // detect it. | |
| 257 if (_isDeadCont(node)) { | 357 if (_isDeadCont(node)) { |
| 258 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); | 358 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
| 359 } else if (_isBetaContLin(node)){ | |
| 360 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | |
| 259 } else if (_isEtaCont(node)) { | 361 } else if (_isEtaCont(node)) { |
| 260 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); | 362 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
| 261 } else if (_isBetaContLin(node)){ | 363 } |
| 262 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | 364 } |
| 365 | |
| 366 void processParameter(Parameter node) { | |
| 367 if (_isDeadParameter(node)) { | |
| 368 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); | |
| 263 } | 369 } |
| 264 } | 370 } |
| 265 } | 371 } |
| 266 | 372 |
| 267 /// Traverses a deleted CPS term, marking nodes that might participate in a | 373 /// Traverses a deleted CPS term, marking nodes that might participate in a |
| 268 /// redex as deleted and adding newly created redexes to the worklist. | 374 /// redex as deleted and adding newly created redexes to the worklist. |
| 269 /// | 375 /// |
| 270 /// Deleted nodes that might participate in a reduction task are marked so that | 376 /// 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 | 377 /// any corresponding tasks can be skipped. Nodes are marked so by setting |
| 272 /// their parent to the deleted sentinel. | 378 /// their parent to the deleted sentinel. |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 291 Node parent = primitive.parent; | 397 Node parent = primitive.parent; |
| 292 // The parent might be the deleted sentinel, or it might be a | 398 // The parent might be the deleted sentinel, or it might be a |
| 293 // Continuation or FunctionDefinition if the primitive is an argument. | 399 // Continuation or FunctionDefinition if the primitive is an argument. |
| 294 if (parent is LetPrim && _isDeadVal(parent)) { | 400 if (parent is LetPrim && _isDeadVal(parent)) { |
| 295 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 401 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
| 296 } | 402 } |
| 297 } else if (reference.definition is Continuation) { | 403 } else if (reference.definition is Continuation) { |
| 298 Continuation cont = reference.definition; | 404 Continuation cont = reference.definition; |
| 299 Node parent = cont.parent; | 405 Node parent = cont.parent; |
| 300 // The parent might be the deleted sentinel, or it might be a | 406 // The parent might be the deleted sentinel, or it might be a |
| 301 // FunctionDefinition if the continuation is the return continuation. | 407 // RunnableBody if the continuation is the return continuation. |
| 302 if (parent is LetCont) { | 408 if (parent is LetCont) { |
| 303 if (cont.isRecursive && cont.hasAtMostOneUse) { | 409 if (cont.isRecursive && cont.hasAtMostOneUse) { |
| 304 // Convert recursive to nonrecursive continuations. If the | 410 // Convert recursive to nonrecursive continuations. If the |
| 305 // continuation is still in use, it is either dead and will be | 411 // continuation is still in use, it is either dead and will be |
| 306 // removed, or it is called nonrecursively outside its body. | 412 // removed, or it is called nonrecursively outside its body. |
| 307 cont.isRecursive = false; | 413 cont.isRecursive = false; |
| 308 } | 414 } |
| 309 if (_isDeadCont(cont)) { | 415 if (_isDeadCont(cont)) { |
| 310 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); | 416 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); |
| 417 } else if (_isBetaContLin(cont)) { | |
| 418 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); | |
| 311 } | 419 } |
| 312 } | 420 } |
| 313 } | 421 } |
| 314 } | 422 } |
| 315 } | 423 } |
| 316 | 424 |
| 317 /// Traverses the CPS term and sets node.parent for each visited node. | 425 /// Traverses the CPS term and sets node.parent for each visited node. |
| 318 class ParentVisitor extends RecursiveVisitor { | 426 class ParentVisitor extends RecursiveVisitor { |
| 319 processFunctionDefinition(FunctionDefinition node) { | 427 processFunctionDefinition(FunctionDefinition node) { |
| 320 node.body.parent = node; | 428 node.body.parent = node; |
| 321 node.parameters.forEach((Definition p) => p.parent = node); | 429 int index = 0; |
| 430 node.parameters.forEach((Parameter parameter) { | |
| 431 parameter.parent = node; | |
| 432 parameter.parent_index = index++; | |
| 433 }); | |
| 322 } | 434 } |
| 323 | 435 |
| 324 processRunnableBody(RunnableBody node) { | 436 processRunnableBody(RunnableBody node) { |
| 437 node.returnContinuation.parent = node; | |
| 325 node.body.parent = node; | 438 node.body.parent = node; |
| 326 } | 439 } |
| 327 | 440 |
| 328 processConstructorDefinition(ConstructorDefinition node) { | 441 processConstructorDefinition(ConstructorDefinition node) { |
| 329 node.body.parent = node; | 442 node.body.parent = node; |
| 330 node.parameters.forEach((Definition p) => p.parent = node); | 443 int index = 0; |
| 444 node.parameters.forEach((Parameter parameter) { | |
| 445 parameter.parent = node; | |
| 446 parameter.parent_index = index++; | |
| 447 }); | |
| 331 node.initializers.forEach((Initializer i) => i.parent = node); | 448 node.initializers.forEach((Initializer i) => i.parent = node); |
| 332 } | 449 } |
| 333 | 450 |
| 334 // Expressions. | 451 // Expressions. |
| 335 | 452 |
| 336 processFieldInitializer(FieldInitializer node) { | 453 processFieldInitializer(FieldInitializer node) { |
| 337 node.body.body.parent = node; | 454 node.body.body.parent = node; |
| 338 } | 455 } |
| 339 | 456 |
| 340 processSuperInitializer(SuperInitializer node) { | 457 processSuperInitializer(SuperInitializer node) { |
| 341 node.arguments.forEach( | 458 node.arguments.forEach( |
| 342 (RunnableBody argument) => argument.body.parent = node); | 459 (RunnableBody argument) => argument.body.parent = node); |
| 343 } | 460 } |
| 344 | 461 |
| 345 processLetPrim(LetPrim node) { | 462 processLetPrim(LetPrim node) { |
| 346 node.primitive.parent = node; | 463 node.primitive.parent = node; |
| 347 node.body.parent = node; | 464 node.body.parent = node; |
| 348 } | 465 } |
| 349 | 466 |
| 350 processLetCont(LetCont node) { | 467 processLetCont(LetCont node) { |
| 351 for (int i = 0; i < node.continuations.length; ++i) { | 468 int index = 0; |
| 352 Continuation cont = node.continuations[i]; | 469 node.continuations.forEach((Continuation continuation) { |
| 353 cont.parent = node; | 470 continuation.parent = node; |
| 354 cont.parent_index = i; | 471 continuation.parent_index = index++; |
| 355 } | 472 }); |
| 356 node.body.parent = node; | 473 node.body.parent = node; |
| 357 } | 474 } |
| 358 | 475 |
| 359 processInvokeStatic(InvokeStatic node) { | 476 processInvokeStatic(InvokeStatic node) { |
| 360 node.arguments.forEach((Reference ref) => ref.parent = node); | 477 node.arguments.forEach((Reference ref) => ref.parent = node); |
| 361 node.continuation.parent = node; | 478 node.continuation.parent = node; |
| 362 } | 479 } |
| 363 | 480 |
| 364 processInvokeContinuation(InvokeContinuation node) { | 481 processInvokeContinuation(InvokeContinuation node) { |
| 365 node.continuation.parent = node; | 482 node.continuation.parent = node; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 entry.key.parent = node; | 537 entry.key.parent = node; |
| 421 entry.value.parent = node; | 538 entry.value.parent = node; |
| 422 }); | 539 }); |
| 423 } | 540 } |
| 424 | 541 |
| 425 processCreateFunction(CreateFunction node) { | 542 processCreateFunction(CreateFunction node) { |
| 426 node.definition.parent = node; | 543 node.definition.parent = node; |
| 427 } | 544 } |
| 428 | 545 |
| 429 processContinuation(Continuation node) { | 546 processContinuation(Continuation node) { |
| 430 node.body.parent = node; | 547 if (node.body != null) node.body.parent = node; |
| 431 node.parameters.forEach((Parameter param) => param.parent = node); | 548 int index = 0; |
| 549 node.parameters.forEach((Parameter parameter) { | |
| 550 parameter.parent = node; | |
| 551 parameter.parent_index = index++; | |
| 552 }); | |
| 432 } | 553 } |
| 433 | 554 |
| 434 // Conditions. | 555 // Conditions. |
| 435 | 556 |
| 436 processIsTrue(IsTrue node) { | 557 processIsTrue(IsTrue node) { |
| 437 node.value.parent = node; | 558 node.value.parent = node; |
| 438 } | 559 } |
| 439 | 560 |
| 440 // JavaScript specific nodes. | 561 // JavaScript specific nodes. |
| 441 | 562 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 470 final String name; | 591 final String name; |
| 471 final int hashCode; | 592 final int hashCode; |
| 472 | 593 |
| 473 const _ReductionKind(this.name, this.hashCode); | 594 const _ReductionKind(this.name, this.hashCode); |
| 474 | 595 |
| 475 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); | 596 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); |
| 476 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); | 597 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); |
| 477 static const _ReductionKind BETA_CONT_LIN = | 598 static const _ReductionKind BETA_CONT_LIN = |
| 478 const _ReductionKind('beta-cont-lin', 2); | 599 const _ReductionKind('beta-cont-lin', 2); |
| 479 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); | 600 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); |
| 601 static const _ReductionKind DEAD_PARAMETER = | |
| 602 const _ReductionKind('dead-parameter', 4); | |
| 480 | 603 |
| 481 String toString() => name; | 604 String toString() => name; |
| 482 } | 605 } |
| 483 | 606 |
| 484 /// Represents a reduction task on the worklist. Implements both hashCode and | 607 /// Represents a reduction task on the worklist. Implements both hashCode and |
| 485 /// operator== since instantiations are used as Set elements. | 608 /// operator== since instantiations are used as Set elements. |
| 486 class _ReductionTask { | 609 class _ReductionTask { |
| 487 final _ReductionKind kind; | 610 final _ReductionKind kind; |
| 488 final Node node; | 611 final Node node; |
| 489 | 612 |
| 490 int get hashCode { | 613 int get hashCode { |
| 491 assert(kind.hashCode < (1 << 2)); | 614 assert(kind.hashCode < (1 << 3)); |
| 492 return (node.hashCode << 2) | kind.hashCode; | 615 return (node.hashCode << 3) | kind.hashCode; |
| 493 } | 616 } |
| 494 | 617 |
| 495 _ReductionTask(this.kind, this.node) { | 618 _ReductionTask(this.kind, this.node) { |
| 496 assert(node is Continuation || node is LetPrim); | 619 assert(node is Continuation || node is LetPrim || node is Parameter); |
| 497 } | 620 } |
| 498 | 621 |
| 499 bool operator==(_ReductionTask that) { | 622 bool operator==(_ReductionTask that) { |
| 500 return (that.kind == this.kind && that.node == this.node); | 623 return (that.kind == this.kind && that.node == this.node); |
| 501 } | 624 } |
| 502 | 625 |
| 503 String toString() => "$kind: $node"; | 626 String toString() => "$kind: $node"; |
| 504 } | 627 } |
| 505 | 628 |
| 506 /// A dummy class used solely to mark nodes as deleted once they are removed | 629 /// A dummy class used solely to mark nodes as deleted once they are removed |
| 507 /// from a term. | 630 /// from a term. |
| 508 class _DeletedNode extends Node { | 631 class _DeletedNode extends Node { |
| 509 accept(_) => null; | 632 accept(_) => null; |
| 510 } | 633 } |
| OLD | NEW |