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

Side by Side Diff: pkg/compiler/lib/src/js_backend/codegen/codegen.dart

Issue 1232083002: dart2js cps: Emit unlabeled breaks and continues when possible. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Update test Created 5 years, 5 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
« no previous file with comments | « no previous file | tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 library code_generator; 5 library code_generator;
6 6
7 import 'glue.dart'; 7 import 'glue.dart';
8 8
9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir;
10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator; 10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator;
(...skipping 30 matching lines...) Expand all
41 41
42 /// Maps local constants to their name. 42 /// Maps local constants to their name.
43 Maplet<VariableElement, String> constantNames = 43 Maplet<VariableElement, String> constantNames =
44 new Maplet<VariableElement, String>(); 44 new Maplet<VariableElement, String>();
45 45
46 /// Variable names that have already been used. Used to avoid name clashes. 46 /// Variable names that have already been used. Used to avoid name clashes.
47 Set<String> usedVariableNames = new Set<String>(); 47 Set<String> usedVariableNames = new Set<String>();
48 48
49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); 49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack();
50 50
51 /// Stacks whose top element is the current target of an unlabeled break
52 /// or continue. For continues, this is the loop node itself.
53 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack();
54 final tree_ir.FallthroughStack shortContinue =
55 new tree_ir.FallthroughStack();
56
51 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); 57 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>();
52 58
53 List<js.Statement> accumulator = new List<js.Statement>(); 59 List<js.Statement> accumulator = new List<js.Statement>();
54 60
55 CodeGenerator(this.glue, this.registry); 61 CodeGenerator(this.glue, this.registry);
56 62
57 /// Generates JavaScript code for the body of [function]. 63 /// Generates JavaScript code for the body of [function].
58 js.Fun buildFunction(tree_ir.FunctionDefinition function) { 64 js.Fun buildFunction(tree_ir.FunctionDefinition function) {
59 currentFunction = function.element; 65 currentFunction = function.element;
60 visitStatement(function.body); 66 visitStatement(function.body);
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 @override 389 @override
384 js.Expression visitAssign(tree_ir.Assign node) { 390 js.Expression visitAssign(tree_ir.Assign node) {
385 return new js.Assignment( 391 return new js.Assignment(
386 buildVariableAccess(node.variable), 392 buildVariableAccess(node.variable),
387 visitExpression(node.value)); 393 visitExpression(node.value));
388 } 394 }
389 395
390 @override 396 @override
391 void visitContinue(tree_ir.Continue node) { 397 void visitContinue(tree_ir.Continue node) {
392 tree_ir.Statement next = fallthrough.target; 398 tree_ir.Statement next = fallthrough.target;
393 if (node.target.binding == next) { 399 if (node.target.binding == next ||
394 // Fall through to continue target 400 next is tree_ir.Continue && node.target == next.target) {
401 // Fall through to continue target or to equivalent continue.
395 fallthrough.use(); 402 fallthrough.use();
396 } else if (next is tree_ir.Continue && next.target == node.target) { 403 } else if (node.target.binding == shortContinue.target) {
397 // Fall through to equivalent continue 404 // The target is the immediately enclosing loop.
398 fallthrough.use(); 405 shortContinue.use();
406 accumulator.add(new js.Continue(null));
399 } else { 407 } else {
400 usedLabels.add(node.target); 408 usedLabels.add(node.target);
401 accumulator.add(new js.Continue(node.target.name)); 409 accumulator.add(new js.Continue(node.target.name));
402 } 410 }
403 } 411 }
404 412
413 /// True if [other] is the target of [node] or is a [Break] with the same
414 /// target. This means jumping to [other] is equivalent to executing [node].
415 bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) {
416 return node.target.binding.next == other ||
417 other is tree_ir.Break && node.target == other.target;
418 }
419
405 @override 420 @override
406 void visitBreak(tree_ir.Break node) { 421 void visitBreak(tree_ir.Break node) {
407 tree_ir.Statement next = fallthrough.target; 422 if (isEffectiveBreakTarget(node, fallthrough.target)) {
408 if (node.target.binding.next == next) { 423 // Fall through to break target or to equivalent break.
409 // Fall through to break target
410 fallthrough.use(); 424 fallthrough.use();
411 } else if (next is tree_ir.Break && next.target == node.target) { 425 } else if (isEffectiveBreakTarget(node, shortBreak.target)) {
412 // Fall through to equivalent break 426 // Unlabeled break to the break target or to an equivalent break.
413 fallthrough.use(); 427 shortBreak.use();
428 accumulator.add(new js.Break(null));
414 } else { 429 } else {
415 usedLabels.add(node.target); 430 usedLabels.add(node.target);
416 accumulator.add(new js.Break(node.target.name)); 431 accumulator.add(new js.Break(node.target.name));
417 } 432 }
418 } 433 }
419 434
420 @override 435 @override
421 void visitExpressionStatement(tree_ir.ExpressionStatement node) { 436 void visitExpressionStatement(tree_ir.ExpressionStatement node) {
422 accumulator.add(new js.ExpressionStatement( 437 accumulator.add(new js.ExpressionStatement(
423 visitExpression(node.expression))); 438 visitExpression(node.expression)));
(...skipping 12 matching lines...) Expand all
436 } else { 451 } else {
437 // The 'then' body cannot complete normally, so emit a short 'if' 452 // The 'then' body cannot complete normally, so emit a short 'if'
438 // and put the 'else' body after it. 453 // and put the 'else' body after it.
439 accumulator.add(new js.If.noElse(condition, thenBody)); 454 accumulator.add(new js.If.noElse(condition, thenBody));
440 visitStatement(node.elseStatement); 455 visitStatement(node.elseStatement);
441 } 456 }
442 } 457 }
443 458
444 @override 459 @override
445 void visitLabeledStatement(tree_ir.LabeledStatement node) { 460 void visitLabeledStatement(tree_ir.LabeledStatement node) {
446 accumulator.add(buildLabeled(() => buildBodyStatement(node.body), 461 fallthrough.push(node.next);
447 node.label, 462 js.Statement body = buildBodyStatement(node.body);
448 node.next)); 463 fallthrough.pop();
464 accumulator.add(insertLabel(node.label, body));
449 visitStatement(node.next); 465 visitStatement(node.next);
450 } 466 }
451 467
452 js.Statement buildLabeled(js.Statement buildBody(), 468 /// Wraps a node in a labeled statement unless the label is unused.
453 tree_ir.Label label, 469 js.Statement insertLabel(tree_ir.Label label, js.Statement node) {
454 tree_ir.Statement fallthroughStatement) {
455 fallthrough.push(fallthroughStatement);
456 js.Statement result = buildBody();
457 if (usedLabels.remove(label)) { 470 if (usedLabels.remove(label)) {
458 result = new js.LabeledStatement(label.name, result); 471 return new js.LabeledStatement(label.name, node);
472 } else {
473 return node;
459 } 474 }
460 fallthrough.pop();
461 return result;
462 } 475 }
463 476
464 /// Returns the current [accumulator] wrapped in a block if neccessary. 477 /// Returns the current [accumulator] wrapped in a block if neccessary.
465 js.Statement _bodyAsStatement() { 478 js.Statement _bodyAsStatement() {
466 if (accumulator.length == 0) { 479 if (accumulator.length == 0) {
467 return new js.EmptyStatement(); 480 return new js.EmptyStatement();
468 } 481 }
469 if (accumulator.length == 1) { 482 if (accumulator.length == 1) {
470 return accumulator.single; 483 return accumulator.single;
471 } 484 }
(...skipping 12 matching lines...) Expand all
484 497
485 js.Block buildBodyBlock(tree_ir.Statement statement) { 498 js.Block buildBodyBlock(tree_ir.Statement statement) {
486 List<js.Statement> savedAccumulator = accumulator; 499 List<js.Statement> savedAccumulator = accumulator;
487 accumulator = <js.Statement>[]; 500 accumulator = <js.Statement>[];
488 visitStatement(statement); 501 visitStatement(statement);
489 js.Statement result = new js.Block(accumulator); 502 js.Statement result = new js.Block(accumulator);
490 accumulator = savedAccumulator; 503 accumulator = savedAccumulator;
491 return result; 504 return result;
492 } 505 }
493 506
494 js.Statement buildWhile(js.Expression condition,
495 tree_ir.Statement body,
496 tree_ir.Label label,
497 tree_ir.Statement fallthroughStatement) {
498 return buildLabeled(() => new js.While(condition, buildBodyStatement(body)),
499 label,
500 fallthroughStatement);
501 }
502
503 @override 507 @override
504 void visitWhileCondition(tree_ir.WhileCondition node) { 508 void visitWhileCondition(tree_ir.WhileCondition node) {
505 accumulator.add( 509 js.Expression condition = visitExpression(node.condition);
506 buildWhile(visitExpression(node.condition), 510 shortBreak.push(node.next);
507 node.body, 511 shortContinue.push(node);
508 node.label, 512 fallthrough.push(node);
509 node)); 513 js.Statement jsBody = buildBodyStatement(node.body);
514 fallthrough.pop();
515 shortContinue.pop();
516 shortBreak.pop();
517 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody)));
510 visitStatement(node.next); 518 visitStatement(node.next);
511 } 519 }
512 520
513 @override 521 @override
514 void visitWhileTrue(tree_ir.WhileTrue node) { 522 void visitWhileTrue(tree_ir.WhileTrue node) {
515 accumulator.add( 523 js.Expression condition = new js.LiteralBool(true);
516 buildWhile(new js.LiteralBool(true), node.body, node.label, node)); 524 // A short break in the while will jump to the current fallthrough target.
525 shortBreak.push(fallthrough.target);
526 shortContinue.push(node);
527 fallthrough.push(node);
528 js.Statement jsBody = buildBodyStatement(node.body);
529 fallthrough.pop();
530 shortContinue.pop();
531 if (shortBreak.useCount > 0) {
532 // Short breaks use the current fallthrough target.
533 fallthrough.use();
534 }
535 shortBreak.pop();
536 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody)));
517 } 537 }
518 538
519 bool isNull(tree_ir.Expression node) { 539 bool isNull(tree_ir.Expression node) {
520 return node is tree_ir.Constant && node.value.isNull; 540 return node is tree_ir.Constant && node.value.isNull;
521 } 541 }
522 542
523 @override 543 @override
524 void visitReturn(tree_ir.Return node) { 544 void visitReturn(tree_ir.Return node) {
525 if (isNull(node.value) && fallthrough.target == null) { 545 if (isNull(node.value) && fallthrough.target == null) {
526 // Do nothing. Implicitly return JS undefined by falling over the end. 546 // Do nothing. Implicitly return JS undefined by falling over the end.
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 return js.js("typeof # === 'number' && Math.floor(#) === #", args); 806 return js.js("typeof # === 'number' && Math.floor(#) === #", args);
787 } 807 }
788 } 808 }
789 809
790 visitFunctionExpression(tree_ir.FunctionExpression node) { 810 visitFunctionExpression(tree_ir.FunctionExpression node) {
791 // FunctionExpressions are currently unused. 811 // FunctionExpressions are currently unused.
792 // We might need them if we want to emit raw JS nested functions. 812 // We might need them if we want to emit raw JS nested functions.
793 throw 'FunctionExpressions should not be used'; 813 throw 'FunctionExpressions should not be used';
794 } 814 }
795 } 815 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698