| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 dart2js.ir_builder; | 5 library dart2js.ir_builder; |
| 6 | 6 |
| 7 import '../constants/expressions.dart'; | 7 import '../constants/expressions.dart'; |
| 8 import '../constants/values.dart' show PrimitiveConstantValue; | 8 import '../constants/values.dart' show PrimitiveConstantValue; |
| 9 import '../dart_backend/dart_backend.dart' show DartBackend; | 9 import '../dart_backend/dart_backend.dart' show DartBackend; |
| 10 import '../dart_types.dart'; | 10 import '../dart_types.dart'; |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 | 155 |
| 156 /// Builds and returns the [ir.Node] for [node] or returns `null` if | 156 /// Builds and returns the [ir.Node] for [node] or returns `null` if |
| 157 /// [node] is `null`. | 157 /// [node] is `null`. |
| 158 ir.Node build(N node) => node != null ? visit(node) : null; | 158 ir.Node build(N node) => node != null ? visit(node) : null; |
| 159 | 159 |
| 160 /// Returns a closure that takes an [IrBuilder] and builds [node] in its | 160 /// Returns a closure that takes an [IrBuilder] and builds [node] in its |
| 161 /// context using [build]. | 161 /// context using [build]. |
| 162 SubbuildFunction subbuild(N node) { | 162 SubbuildFunction subbuild(N node) { |
| 163 return (IrBuilder builder) => withBuilder(builder, () => build(node)); | 163 return (IrBuilder builder) => withBuilder(builder, () => build(node)); |
| 164 } | 164 } |
| 165 |
| 166 /// Returns a closure that takes an [IrBuilder] and builds the sequence of |
| 167 /// [nodes] in its context using [build]. |
| 168 // TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses |
| 169 // `List` instead of `Link`. |
| 170 SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) { |
| 171 return (IrBuilder builder) { |
| 172 return withBuilder(builder, () => builder.buildSequence(nodes, build)); |
| 173 }; |
| 174 } |
| 165 } | 175 } |
| 166 | 176 |
| 167 /// Shared state between nested builders. | 177 /// Shared state between nested builders. |
| 168 class IrBuilderSharedState { | 178 class IrBuilderSharedState { |
| 169 final ConstantSystem constantSystem; | 179 final ConstantSystem constantSystem; |
| 170 | 180 |
| 171 /// A stack of collectors for breaks. | 181 /// A stack of collectors for breaks. |
| 172 final List<JumpCollector> breakCollectors = <JumpCollector>[]; | 182 final List<JumpCollector> breakCollectors = <JumpCollector>[]; |
| 173 | 183 |
| 174 /// A stack of collectors for continues. | 184 /// A stack of collectors for continues. |
| (...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 612 } else if (elseBuilder.isOpen) { | 622 } else if (elseBuilder.isOpen) { |
| 613 _current = | 623 _current = |
| 614 (elseBuilder._root == null) ? letElse : elseBuilder._current; | 624 (elseBuilder._root == null) ? letElse : elseBuilder._current; |
| 615 environment = elseBuilder.environment; | 625 environment = elseBuilder.environment; |
| 616 } else { | 626 } else { |
| 617 _current = null; | 627 _current = null; |
| 618 } | 628 } |
| 619 } | 629 } |
| 620 } | 630 } |
| 621 | 631 |
| 632 /// Invoke a join-point continuation that contains arguments for all local |
| 633 /// variables. |
| 634 /// |
| 635 /// Given the continuation and a list of uninitialized invocations, fill |
| 636 /// in each invocation with the continuation and appropriate arguments. |
| 637 void invokeFullJoin(ir.Continuation join, |
| 638 JumpCollector jumps, |
| 639 {recursive: false}) { |
| 640 join.isRecursive = recursive; |
| 641 for (int i = 0; i < jumps.length; ++i) { |
| 642 Environment currentEnvironment = jumps.environments[i]; |
| 643 ir.InvokeContinuation invoke = jumps.invocations[i]; |
| 644 invoke.continuation = new ir.Reference(join); |
| 645 invoke.arguments = new List<ir.Reference>.generate( |
| 646 join.parameters.length, |
| 647 (i) => new ir.Reference(currentEnvironment[i])); |
| 648 invoke.isRecursive = recursive; |
| 649 } |
| 650 } |
| 651 |
| 652 /// Creates a for loop in which the initializer, condition, body, update are |
| 653 /// created by [buildInitializer], [buildCondition], [buildBody] and |
| 654 /// [buildUpdate], respectively. |
| 655 /// |
| 656 /// The jump [target] is used to identify which `break` and `continue` |
| 657 /// statements that have this `for` statement as their target. |
| 658 void buildFor({SubbuildFunction buildInitializer, |
| 659 SubbuildFunction buildCondition, |
| 660 SubbuildFunction buildBody, |
| 661 SubbuildFunction buildUpdate, |
| 662 JumpTarget target}) { |
| 663 assert(isOpen); |
| 664 |
| 665 // For loops use four named continuations: the entry to the condition, |
| 666 // the entry to the body, the loop exit, and the loop successor (break). |
| 667 // The CPS translation of |
| 668 // [[for (initializer; condition; update) body; successor]] is: |
| 669 // |
| 670 // [[initializer]]; |
| 671 // let cont loop(x, ...) = |
| 672 // let prim cond = [[condition]] in |
| 673 // let cont break() = [[successor]] in |
| 674 // let cont exit() = break(v, ...) in |
| 675 // let cont body() = |
| 676 // let cont continue(x, ...) = [[update]]; loop(v, ...) in |
| 677 // [[body]]; continue(v, ...) in |
| 678 // branch cond (body, exit) in |
| 679 // loop(v, ...) |
| 680 // |
| 681 // If there are no breaks in the body, the break continuation is inlined |
| 682 // in the exit continuation (i.e., the translation of the successor |
| 683 // statement occurs in the exit continuation). If there is only one |
| 684 // invocation of the continue continuation (i.e., no continues in the |
| 685 // body), the continue continuation is inlined in the body. |
| 686 |
| 687 buildInitializer(this); |
| 688 |
| 689 IrBuilder condBuilder = new IrBuilder.recursive(this); |
| 690 ir.Primitive condition = buildCondition(condBuilder); |
| 691 if (condition == null) { |
| 692 // If the condition is empty then the body is entered unconditionally. |
| 693 condition = condBuilder.buildBooleanLiteral(true); |
| 694 } |
| 695 |
| 696 JumpCollector breakCollector = new JumpCollector(target); |
| 697 JumpCollector continueCollector = new JumpCollector(target); |
| 698 state.breakCollectors.add(breakCollector); |
| 699 state.continueCollectors.add(continueCollector); |
| 700 |
| 701 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
| 702 buildBody(bodyBuilder); |
| 703 assert(state.breakCollectors.last == breakCollector); |
| 704 assert(state.continueCollectors.last == continueCollector); |
| 705 state.breakCollectors.removeLast(); |
| 706 state.continueCollectors.removeLast(); |
| 707 |
| 708 // The binding of the continue continuation should occur as late as |
| 709 // possible, that is, at the nearest common ancestor of all the continue |
| 710 // sites in the body. However, that is difficult to compute here, so it |
| 711 // is instead placed just outside the body of the body continuation. |
| 712 bool hasContinues = !continueCollector.isEmpty; |
| 713 IrBuilder updateBuilder = hasContinues |
| 714 ? new IrBuilder.recursive(condBuilder) |
| 715 : bodyBuilder; |
| 716 buildUpdate(updateBuilder); |
| 717 |
| 718 // Create body entry and loop exit continuations and a branch to them. |
| 719 ir.Continuation bodyContinuation = new ir.Continuation([]); |
| 720 ir.Continuation exitContinuation = new ir.Continuation([]); |
| 721 ir.LetCont branch = |
| 722 new ir.LetCont(exitContinuation, |
| 723 new ir.LetCont(bodyContinuation, |
| 724 new ir.Branch(new ir.IsTrue(condition), |
| 725 bodyContinuation, |
| 726 exitContinuation))); |
| 727 // If there are breaks in the body, then there must be a join-point |
| 728 // continuation for the normal exit and the breaks. |
| 729 bool hasBreaks = !breakCollector.isEmpty; |
| 730 ir.LetCont letJoin; |
| 731 if (hasBreaks) { |
| 732 letJoin = new ir.LetCont(null, branch); |
| 733 condBuilder.add(letJoin); |
| 734 condBuilder._current = branch; |
| 735 } else { |
| 736 condBuilder.add(branch); |
| 737 } |
| 738 ir.Continuation continueContinuation; |
| 739 if (hasContinues) { |
| 740 // If there are continues in the body, we need a named continue |
| 741 // continuation as a join point. |
| 742 continueContinuation = new ir.Continuation(updateBuilder._parameters); |
| 743 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); |
| 744 invokeFullJoin(continueContinuation, continueCollector); |
| 745 } |
| 746 ir.Continuation loopContinuation = |
| 747 new ir.Continuation(condBuilder._parameters); |
| 748 if (updateBuilder.isOpen) { |
| 749 JumpCollector backEdges = new JumpCollector(null); |
| 750 backEdges.addJump(updateBuilder); |
| 751 invokeFullJoin(loopContinuation, backEdges, recursive: true); |
| 752 } |
| 753 |
| 754 // Fill in the body and possible continue continuation bodies. Do this |
| 755 // only after it is guaranteed that they are not empty. |
| 756 if (hasContinues) { |
| 757 continueContinuation.body = updateBuilder._root; |
| 758 bodyContinuation.body = |
| 759 new ir.LetCont(continueContinuation, bodyBuilder._root); |
| 760 } else { |
| 761 bodyContinuation.body = bodyBuilder._root; |
| 762 } |
| 763 |
| 764 loopContinuation.body = condBuilder._root; |
| 765 add(new ir.LetCont(loopContinuation, |
| 766 new ir.InvokeContinuation(loopContinuation, |
| 767 environment.index2value))); |
| 768 if (hasBreaks) { |
| 769 _current = branch; |
| 770 environment = condBuilder.environment; |
| 771 breakCollector.addJump(this); |
| 772 letJoin.continuation = createJoin(environment.length, breakCollector); |
| 773 _current = letJoin; |
| 774 } else { |
| 775 _current = condBuilder._current; |
| 776 environment = condBuilder.environment; |
| 777 } |
| 778 } |
| 779 |
| 622 /// Create a return statement `return value;` or `return;` if [value] is | 780 /// Create a return statement `return value;` or `return;` if [value] is |
| 623 /// null. | 781 /// null. |
| 624 void buildReturn([ir.Primitive value]) { | 782 void buildReturn([ir.Primitive value]) { |
| 625 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | 783 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] |
| 626 // where (C', x) = Build(e, C) | 784 // where (C', x) = Build(e, C) |
| 627 // | 785 // |
| 628 // Return without a subexpression is translated as if it were return null. | 786 // Return without a subexpression is translated as if it were return null. |
| 629 assert(isOpen); | 787 assert(isOpen); |
| 630 if (value == null) { | 788 if (value == null) { |
| 631 value = buildNullLiteral(); | 789 value = buildNullLiteral(); |
| 632 } | 790 } |
| 633 add(new ir.InvokeContinuation(state.returnContinuation, [value])); | 791 add(new ir.InvokeContinuation(state.returnContinuation, [value])); |
| 634 _current = null; | 792 _current = null; |
| 635 } | 793 } |
| 636 | 794 |
| 637 /// Create a blocks of [statements] by applying [build] to all reachable | 795 /// Create a blocks of [statements] by applying [build] to all reachable |
| 638 /// statements. | 796 /// statements. The first statement is assumed to be reachable. |
| 639 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses | 797 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses |
| 640 // `List` instead of `Link`. | 798 // `List` instead of `Link`. |
| 641 void buildBlock(var statements, build(statement)) { | 799 void buildBlock(var statements, build(statement)) { |
| 642 // Build(Block(stamements), C) = C' | 800 // Build(Block(stamements), C) = C' |
| 643 // where C' = statements.fold(Build, C) | 801 // where C' = statements.fold(Build, C) |
| 644 assert(isOpen); | 802 assert(isOpen); |
| 645 for (var statement in statements) { | 803 return buildSequence(statements, build); |
| 646 build(statement); | 804 } |
| 805 |
| 806 /// Creates a sequence of [nodes] by applying [build] to all reachable nodes. |
| 807 /// |
| 808 /// The first node in the sequence does not need to be reachable. |
| 809 // TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses |
| 810 // `List` instead of `Link`. |
| 811 void buildSequence(var nodes, build(node)) { |
| 812 for (var node in nodes) { |
| 647 if (!isOpen) return; | 813 if (!isOpen) return; |
| 814 build(node); |
| 648 } | 815 } |
| 649 } | 816 } |
| 650 | 817 |
| 651 | 818 |
| 652 // Build(BreakStatement L, C) = C[InvokeContinuation(...)] | 819 // Build(BreakStatement L, C) = C[InvokeContinuation(...)] |
| 653 // | 820 // |
| 654 // The continuation and arguments are filled in later after translating | 821 // The continuation and arguments are filled in later after translating |
| 655 // the body containing the break. | 822 // the body containing the break. |
| 656 bool buildBreak(JumpTarget target) { | 823 bool buildBreak(JumpTarget target) { |
| 657 return buildJumpInternal(target, state.breakCollectors); | 824 return buildJumpInternal(target, state.breakCollectors); |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 893 index = 0; | 1060 index = 0; |
| 894 for (int i = 0; i < environment.length; ++i) { | 1061 for (int i = 0; i < environment.length; ++i) { |
| 895 if (common[i] == null) { | 1062 if (common[i] == null) { |
| 896 environment.index2value[i] = parameters[index++]; | 1063 environment.index2value[i] = parameters[index++]; |
| 897 } | 1064 } |
| 898 } | 1065 } |
| 899 | 1066 |
| 900 return join; | 1067 return join; |
| 901 } | 1068 } |
| 902 } | 1069 } |
| OLD | NEW |