Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(236)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart

Issue 869223003: Revert "Add a shrinking reduction for dead continuation parameters." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart ('k') | tests/compiler/dart2js/backend_dart/opt_constprop_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698