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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart

Issue 651163004: Make cps_ir_builder_visitor a part. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 2 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
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.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) 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';
11 import '../dart2jslib.dart'; 11 import '../dart2jslib.dart';
12 import '../elements/elements.dart'; 12 import '../elements/elements.dart';
13 import '../source_file.dart'; 13 import '../source_file.dart';
14 import '../tree/tree.dart' as ast; 14 import '../tree/tree.dart' as ast;
15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; 15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator;
16 import '../universe/universe.dart' show SelectorKind; 16 import '../universe/universe.dart' show SelectorKind;
17 import 'cps_ir_nodes.dart' as ir; 17 import 'cps_ir_nodes.dart' as ir;
18 18
19 /** 19 part 'cps_ir_builder_visitor.dart';
20 * This task iterates through all resolved elements and builds [ir.Node]s. The
21 * nodes are stored in the [nodes] map and accessible through [hasIr] and
22 * [getIr].
23 *
24 * The functionality of the IrNodes is added gradually, therefore elements might
25 * have an IR or not, depending on the language features that are used. For
26 * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not
27 * used in the rest of the compilation. This is ensured by setting the element's
28 * cached tree to `null` and also breaking the token stream to crash future
29 * attempts to parse.
30 *
31 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are
32 * then translated into the SSA form for optimizations and code generation.
33 * Long-term, once the IR supports the full language, the backend can be
34 * re-implemented to work directly on the IR.
35 */
36 class IrBuilderTask extends CompilerTask {
37 final Map<Element, ir.FunctionDefinition> nodes =
38 <Element, ir.FunctionDefinition>{};
39
40 IrBuilderTask(Compiler compiler) : super(compiler);
41
42 String get name => 'IR builder';
43
44 bool hasIr(Element element) => nodes.containsKey(element.implementation);
45
46 ir.FunctionDefinition getIr(Element element) => nodes[element.implementation];
47
48 void buildNodes({bool useNewBackend: false}) {
49 if (!irEnabled(useNewBackend: useNewBackend)) return;
50 measure(() {
51 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements;
52 resolved.forEach((AstElement element) {
53 if (canBuild(element)) {
54 TreeElements elementsMapping = element.resolvedAst.elements;
55 element = element.implementation;
56 compiler.withCurrentElement(element, () {
57 SourceFile sourceFile = elementSourceFile(element);
58 IrBuilderVisitor builder =
59 new IrBuilderVisitor(elementsMapping, compiler, sourceFile);
60 ir.FunctionDefinition function;
61 function = builder.buildFunction(element);
62
63 if (function != null) {
64 nodes[element] = function;
65 compiler.tracer.traceCompilation(element.name, null);
66 compiler.tracer.traceGraph("IR Builder", function);
67 }
68 });
69 }
70 });
71 });
72 }
73
74 bool irEnabled({bool useNewBackend: false}) {
75 // TODO(sigurdm,kmillikin): Support checked-mode checks.
76 return (useNewBackend || const bool.fromEnvironment('USE_NEW_BACKEND')) &&
77 compiler.backend is DartBackend &&
78 !compiler.enableTypeAssertions &&
79 !compiler.enableConcreteTypeInference;
80 }
81
82 bool canBuild(Element element) {
83 FunctionElement function = element.asFunctionElement();
84 // TODO(kmillikin,sigurdm): support lazy field initializers.
85 if (function == null) return false;
86
87 if (!compiler.backend.shouldOutput(function)) return false;
88
89 assert(invariant(element, !function.isNative));
90
91 // TODO(kmillikin,sigurdm): Support constructors.
92 if (function is ConstructorElement) return false;
93
94 return true;
95 }
96
97 bool get inCheckedMode {
98 bool result = false;
99 assert((result = true));
100 return result;
101 }
102
103 SourceFile elementSourceFile(Element element) {
104 if (element is FunctionElement) {
105 FunctionElement functionElement = element;
106 if (functionElement.patch != null) element = functionElement.patch;
107 }
108 return element.compilationUnit.script.file;
109 }
110 }
111
112 class _GetterElements {
113 ir.Primitive result;
114 ir.Primitive index;
115 ir.Primitive receiver;
116
117 _GetterElements({this.result, this.index, this.receiver}) ;
118 }
119 20
120 /// A mapping from variable elements to their compile-time values. 21 /// A mapping from variable elements to their compile-time values.
121 /// 22 ///
122 /// Map elements denoted by parameters and local variables to the 23 /// Map elements denoted by parameters and local variables to the
123 /// [ir.Primitive] that is their value. Parameters and locals are 24 /// [ir.Primitive] that is their value. Parameters and locals are
124 /// assigned indexes which can be used to refer to them. 25 /// assigned indexes which can be used to refer to them.
125 class Environment { 26 class Environment {
126 /// A map from elements to their environment index. 27 /// A map from elements to their environment index.
127 final Map<Element, int> variable2index; 28 final Map<Element, int> variable2index;
128 29
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 assert(isOpen); 489 assert(isOpen);
589 for (JumpCollector collector in collectors) { 490 for (JumpCollector collector in collectors) {
590 if (target == collector.target) { 491 if (target == collector.target) {
591 collector.addJump(this); 492 collector.addJump(this);
592 return true; 493 return true;
593 } 494 }
594 } 495 }
595 return false; 496 return false;
596 } 497 }
597 } 498 }
598
599 /**
600 * A tree visitor that builds [IrNodes]. The visit methods add statements using
601 * to the [builder] and return the last added statement for trees that represent
602 * an expression.
603 */
604 class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
605 with IrBuilderMixin {
606 final Compiler compiler;
607 final SourceFile sourceFile;
608
609 // In SSA terms, join-point continuation parameters are the phis and the
610 // continuation invocation arguments are the corresponding phi inputs. To
611 // support name introduction and renaming for source level variables, we use
612 // nested (delimited) visitors for constructing subparts of the IR that will
613 // need renaming. Each source variable is assigned an index.
614 //
615 // Each nested visitor maintains a list of free variable uses in the body.
616 // These are implemented as a list of parameters, each with their own use
617 // list of references. When the delimited subexpression is plugged into the
618 // surrounding context, the free occurrences can be captured or become free
619 // occurrences in the next outer delimited subexpression.
620 //
621 // Each nested visitor maintains a list that maps indexes of variables
622 // assigned in the delimited subexpression to their reaching definition ---
623 // that is, the definition in effect at the hole in 'current'. These are
624 // used to determine if a join-point continuation needs to be passed
625 // arguments, and what the arguments are.
626
627 /// Construct a top-level visitor.
628 IrBuilderVisitor(TreeElements elements, this.compiler, this.sourceFile)
629 : super(elements);
630
631 /**
632 * Builds the [ir.FunctionDefinition] for a function element. In case the
633 * function uses features that cannot be expressed in the IR, this function
634 * returns `null`.
635 */
636 ir.FunctionDefinition buildFunction(FunctionElement functionElement) {
637 return nullIfGiveup(() => buildFunctionInternal(functionElement));
638 }
639
640 ir.FunctionDefinition buildFunctionInternal(FunctionElement element) {
641 assert(invariant(element, element.isImplementation));
642 ast.FunctionExpression function = element.node;
643 assert(function != null);
644 assert(!function.modifiers.isExternal);
645 assert(elements[function] != null);
646
647 DetectClosureVariables closureLocals = new DetectClosureVariables(elements);
648 closureLocals.visit(function);
649
650 return withBuilder(
651 new IrBuilder(compiler.backend.constantSystem,
652 element, closureLocals.usedFromClosure),
653 () {
654 FunctionSignature signature = element.functionSignature;
655 signature.orderedForEachParameter((ParameterElement parameterElement) {
656 irBuilder.createParameter(
657 parameterElement,
658 isClosureVariable: isClosureVariable(parameterElement));
659 });
660
661 List<ConstantExpression> defaults = new List<ConstantExpression>();
662 signature.orderedOptionalParameters.forEach((ParameterElement element) {
663 defaults.add(getConstantForVariable(element));
664 });
665
666 visit(function.body);
667 return irBuilder.buildFunctionDefinition(element, defaults);
668 });
669 }
670
671 ir.Primitive visit(ast.Node node) => node.accept(this);
672
673 // ==== Statements ====
674 // Build(Block(stamements), C) = C'
675 // where C' = statements.fold(Build, C)
676 ir.Primitive visitBlock(ast.Block node) {
677 assert(irBuilder.isOpen);
678 for (ast.Node n in node.statements.nodes) {
679 visit(n);
680 if (!irBuilder.isOpen) return null;
681 }
682 return null;
683 }
684
685 ir.Primitive visitBreakStatement(ast.BreakStatement node) {
686 if (!irBuilder.buildBreak(elements.getTargetOf(node))) {
687 compiler.internalError(node, "'break' target not found");
688 }
689 return null;
690 }
691
692 ir.Primitive visitContinueStatement(ast.ContinueStatement node) {
693 if (!irBuilder.buildContinue(elements.getTargetOf(node))) {
694 compiler.internalError(node, "'continue' target not found");
695 }
696 return null;
697 }
698
699 // Build(EmptyStatement, C) = C
700 ir.Primitive visitEmptyStatement(ast.EmptyStatement node) {
701 assert(irBuilder.isOpen);
702 return null;
703 }
704
705 // Build(ExpressionStatement(e), C) = C'
706 // where (C', _) = Build(e, C)
707 ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) {
708 assert(irBuilder.isOpen);
709 visit(node.expression);
710 return null;
711 }
712
713
714 /// Create a non-recursive join-point continuation.
715 ///
716 /// Given the environment length at the join point and a list of
717 /// jumps that should reach the join point, create a join-point
718 /// continuation. The join-point continuation has a parameter for each
719 /// variable that has different values reaching on different paths.
720 ///
721 /// The jumps are uninitialized [ir.InvokeContinuation] expressions.
722 /// They are filled in with the target continuation and appropriate
723 /// arguments.
724 ///
725 /// As a side effect, the environment of this builder is updated to include
726 /// the join-point continuation parameters.
727 ir.Continuation createJoin(int environmentLength, JumpCollector jumps) {
728 assert(jumps.length >= 2);
729
730 // Compute which values are identical on all paths reaching the join.
731 // Handle the common case of a pair of contexts efficiently.
732 Environment first = jumps.environments[0];
733 Environment second = jumps.environments[1];
734 assert(environmentLength <= first.length);
735 assert(environmentLength <= second.length);
736 assert(first.sameDomain(environmentLength, second));
737 // A running count of the join-point parameters.
738 int parameterCount = 0;
739 // The null elements of common correspond to required parameters of the
740 // join-point continuation.
741 List<ir.Primitive> common =
742 new List<ir.Primitive>.generate(environmentLength,
743 (i) {
744 ir.Primitive candidate = first[i];
745 if (second[i] == candidate) {
746 return candidate;
747 } else {
748 ++parameterCount;
749 return null;
750 }
751 });
752 // If there is already a parameter for each variable, the other
753 // environments do not need to be considered.
754 if (parameterCount < environmentLength) {
755 for (int i = 0; i < environmentLength; ++i) {
756 ir.Primitive candidate = common[i];
757 if (candidate == null) continue;
758 for (Environment current in jumps.environments.skip(2)) {
759 assert(environmentLength <= current.length);
760 assert(first.sameDomain(environmentLength, current));
761 if (candidate != current[i]) {
762 common[i] = null;
763 ++parameterCount;
764 break;
765 }
766 }
767 if (parameterCount >= environmentLength) break;
768 }
769 }
770
771 // Create the join point continuation.
772 List<ir.Parameter> parameters = <ir.Parameter>[];
773 parameters.length = parameterCount;
774 int index = 0;
775 for (int i = 0; i < environmentLength; ++i) {
776 if (common[i] == null) {
777 parameters[index++] = new ir.Parameter(first.index2variable[i]);
778 }
779 }
780 assert(index == parameterCount);
781 ir.Continuation join = new ir.Continuation(parameters);
782
783 // Fill in all the continuation invocations.
784 for (int i = 0; i < jumps.length; ++i) {
785 Environment currentEnvironment = jumps.environments[i];
786 ir.InvokeContinuation invoke = jumps.invocations[i];
787 // Sharing this.environment with one of the invocations will not do
788 // the right thing (this.environment has already been mutated).
789 List<ir.Reference> arguments = <ir.Reference>[];
790 arguments.length = parameterCount;
791 int index = 0;
792 for (int i = 0; i < environmentLength; ++i) {
793 if (common[i] == null) {
794 arguments[index++] = new ir.Reference(currentEnvironment[i]);
795 }
796 }
797 invoke.continuation = new ir.Reference(join);
798 invoke.arguments = arguments;
799 }
800
801 // Mutate this.environment to be the environment at the join point. Do
802 // this after adding the continuation invocations, because this.environment
803 // might be collected by the jump collector and so the old environment
804 // values are needed for the continuation invocation.
805 //
806 // Iterate to environment.length because environmentLength includes values
807 // outside the environment which are 'phantom' variables used for the
808 // values of expressions like &&, ||, and ?:.
809 index = 0;
810 for (int i = 0; i < irBuilder.environment.length; ++i) {
811 if (common[i] == null) {
812 irBuilder.environment.index2value[i] = parameters[index++];
813 }
814 }
815
816 return join;
817 }
818
819 /// Invoke a join-point continuation that contains arguments for all local
820 /// variables.
821 ///
822 /// Given the continuation and a list of uninitialized invocations, fill
823 /// in each invocation with the continuation and appropriate arguments.
824 void invokeFullJoin(ir.Continuation join,
825 JumpCollector jumps,
826 {recursive: false}) {
827 join.isRecursive = recursive;
828 for (int i = 0; i < jumps.length; ++i) {
829 Environment currentEnvironment = jumps.environments[i];
830 ir.InvokeContinuation invoke = jumps.invocations[i];
831 invoke.continuation = new ir.Reference(join);
832 invoke.arguments = new List<ir.Reference>.generate(
833 join.parameters.length,
834 (i) => new ir.Reference(currentEnvironment[i]));
835 invoke.isRecursive = recursive;
836 }
837 }
838
839 ir.Primitive visitFor(ast.For node) {
840 assert(irBuilder.isOpen);
841 // TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop.
842 if (node.initializer is ast.VariableDefinitions) {
843 ast.VariableDefinitions definitions = node.initializer;
844 for (ast.Node definition in definitions.definitions.nodes) {
845 Element element = elements[definition];
846 if (isClosureVariable(element)) {
847 return giveup(definition, 'Closure variable in for loop initializer');
848 }
849 }
850 }
851
852 // For loops use four named continuations: the entry to the condition,
853 // the entry to the body, the loop exit, and the loop successor (break).
854 // The CPS translation of
855 // [[for (initializer; condition; update) body; successor]] is:
856 //
857 // [[initializer]];
858 // let cont loop(x, ...) =
859 // let prim cond = [[condition]] in
860 // let cont break() = [[successor]] in
861 // let cont exit() = break(v, ...) in
862 // let cont body() =
863 // let cont continue(x, ...) = [[update]]; loop(v, ...) in
864 // [[body]]; continue(v, ...) in
865 // branch cond (body, exit) in
866 // loop(v, ...)
867 //
868 // If there are no breaks in the body, the break continuation is inlined
869 // in the exit continuation (i.e., the translation of the successor
870 // statement occurs in the exit continuation). If there is only one
871 // invocation of the continue continuation (i.e., no continues in the
872 // body), the continue continuation is inlined in the body.
873
874 if (node.initializer != null) visit(node.initializer);
875
876 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
877 ir.Primitive condition;
878 if (node.condition == null) {
879 // If the condition is empty then the body is entered unconditionally.
880 condition = irBuilder.makePrimConst(
881 irBuilder.state.constantSystem.createBool(true));
882 condBuilder.add(new ir.LetPrim(condition));
883 } else {
884 condition = withBuilder(condBuilder, () => visit(node.condition));
885 }
886
887 JumpTarget target = elements.getTargetDefinition(node);
888 JumpCollector breakCollector = new JumpCollector(target);
889 JumpCollector continueCollector = new JumpCollector(target);
890 irBuilder.state.breakCollectors.add(breakCollector);
891 irBuilder.state.continueCollectors.add(continueCollector);
892
893 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
894 withBuilder(bodyBuilder, () => visit(node.body));
895 assert(irBuilder.state.breakCollectors.last == breakCollector);
896 assert(irBuilder.state.continueCollectors.last == continueCollector);
897 irBuilder.state.breakCollectors.removeLast();
898 irBuilder.state.continueCollectors.removeLast();
899
900 // The binding of the continue continuation should occur as late as
901 // possible, that is, at the nearest common ancestor of all the continue
902 // sites in the body. However, that is difficult to compute here, so it
903 // is instead placed just outside the body of the body continuation.
904 bool hasContinues = !continueCollector.isEmpty;
905 IrBuilder updateBuilder = hasContinues
906 ? new IrBuilder.recursive(condBuilder)
907 : bodyBuilder;
908 for (ast.Node n in node.update) {
909 if (!updateBuilder.isOpen) break;
910 withBuilder(updateBuilder, () => visit(n));
911 }
912
913 // Create body entry and loop exit continuations and a branch to them.
914 ir.Continuation bodyContinuation = new ir.Continuation([]);
915 ir.Continuation exitContinuation = new ir.Continuation([]);
916 ir.LetCont branch =
917 new ir.LetCont(exitContinuation,
918 new ir.LetCont(bodyContinuation,
919 new ir.Branch(new ir.IsTrue(condition),
920 bodyContinuation,
921 exitContinuation)));
922 // If there are breaks in the body, then there must be a join-point
923 // continuation for the normal exit and the breaks.
924 bool hasBreaks = !breakCollector.isEmpty;
925 ir.LetCont letJoin;
926 if (hasBreaks) {
927 letJoin = new ir.LetCont(null, branch);
928 condBuilder.add(letJoin);
929 condBuilder._current = branch;
930 } else {
931 condBuilder.add(branch);
932 }
933 ir.Continuation continueContinuation;
934 if (hasContinues) {
935 // If there are continues in the body, we need a named continue
936 // continuation as a join point.
937 continueContinuation = new ir.Continuation(updateBuilder._parameters);
938 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
939 invokeFullJoin(continueContinuation, continueCollector);
940 }
941 ir.Continuation loopContinuation =
942 new ir.Continuation(condBuilder._parameters);
943 if (updateBuilder.isOpen) {
944 JumpCollector backEdges = new JumpCollector(null);
945 backEdges.addJump(updateBuilder);
946 invokeFullJoin(loopContinuation, backEdges, recursive: true);
947 }
948
949 // Fill in the body and possible continue continuation bodies. Do this
950 // only after it is guaranteed that they are not empty.
951 if (hasContinues) {
952 continueContinuation.body = updateBuilder._root;
953 bodyContinuation.body =
954 new ir.LetCont(continueContinuation, bodyBuilder._root);
955 } else {
956 bodyContinuation.body = bodyBuilder._root;
957 }
958
959 loopContinuation.body = condBuilder._root;
960 irBuilder.add(new ir.LetCont(loopContinuation,
961 new ir.InvokeContinuation(loopContinuation,
962 irBuilder.environment.index2value)));
963 if (hasBreaks) {
964 irBuilder._current = branch;
965 irBuilder.environment = condBuilder.environment;
966 breakCollector.addJump(irBuilder);
967 letJoin.continuation =
968 createJoin(irBuilder.environment.length, breakCollector);
969 irBuilder._current = letJoin;
970 } else {
971 irBuilder._current = condBuilder._current;
972 irBuilder.environment = condBuilder.environment;
973 }
974 return null;
975 }
976
977 ir.Primitive visitIf(ast.If node) {
978 assert(irBuilder.isOpen);
979 ir.Primitive condition = visit(node.condition);
980
981 // The then and else parts are delimited.
982 IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder);
983 IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder);
984 withBuilder(thenBuilder, () => visit(node.thenPart));
985 if (node.hasElsePart) {
986 withBuilder(elseBuilder, () => visit(node.elsePart));
987 }
988
989 // Build the term
990 // (Result =) let cont then() = [[thenPart]] in
991 // let cont else() = [[elsePart]] in
992 // if condition (then, else)
993 ir.Continuation thenContinuation = new ir.Continuation([]);
994 ir.Continuation elseContinuation = new ir.Continuation([]);
995 ir.Expression letElse =
996 new ir.LetCont(elseContinuation,
997 new ir.Branch(new ir.IsTrue(condition),
998 thenContinuation,
999 elseContinuation));
1000 ir.Expression letThen = new ir.LetCont(thenContinuation, letElse);
1001 ir.Expression result = letThen;
1002
1003 ir.Continuation joinContinuation; // Null if there is no join.
1004 if (thenBuilder.isOpen && elseBuilder.isOpen) {
1005 // There is a join-point continuation. Build the term
1006 // 'let cont join(x, ...) = [] in Result' and plug invocations of the
1007 // join-point continuation into the then and else continuations.
1008 JumpCollector jumps = new JumpCollector(null);
1009 jumps.addJump(thenBuilder);
1010 jumps.addJump(elseBuilder);
1011 joinContinuation = createJoin(irBuilder.environment.length, jumps);
1012 result = new ir.LetCont(joinContinuation, result);
1013 }
1014
1015 // The then or else term root could be null, but not both. If there is
1016 // a join then an InvokeContinuation was just added to both of them. If
1017 // there is no join, then at least one of them is closed and thus has a
1018 // non-null root by the definition of the predicate isClosed. In the
1019 // case that one of them is null, it must be the only one that is open
1020 // and thus contains the new hole in the context. This case is handled
1021 // after the branch is plugged into the current hole.
1022 thenContinuation.body = thenBuilder._root;
1023 elseContinuation.body = elseBuilder._root;
1024
1025 irBuilder.add(result);
1026 if (joinContinuation == null) {
1027 // At least one subexpression is closed.
1028 if (thenBuilder.isOpen) {
1029 irBuilder._current =
1030 (thenBuilder._root == null) ? letThen : thenBuilder._current;
1031 irBuilder.environment = thenBuilder.environment;
1032 } else if (elseBuilder.isOpen) {
1033 irBuilder._current =
1034 (elseBuilder._root == null) ? letElse : elseBuilder._current;
1035 irBuilder.environment = elseBuilder.environment;
1036 } else {
1037 irBuilder._current = null;
1038 }
1039 }
1040 return null;
1041 }
1042
1043 ir.Primitive visitLabeledStatement(ast.LabeledStatement node) {
1044 ast.Statement body = node.statement;
1045 return body is ast.Loop
1046 ? visit(body)
1047 : giveup(node, 'labeled statement');
1048 }
1049
1050 ir.Primitive visitWhile(ast.While node) {
1051 assert(irBuilder.isOpen);
1052 // While loops use four named continuations: the entry to the body, the
1053 // loop exit, the loop back edge (continue), and the loop exit (break).
1054 // The CPS translation of [[while (condition) body; successor]] is:
1055 //
1056 // let cont continue(x, ...) =
1057 // let prim cond = [[condition]] in
1058 // let cont break() = [[successor]] in
1059 // let cont exit() = break(v, ...) in
1060 // let cont body() = [[body]]; continue(v, ...) in
1061 // branch cond (body, exit) in
1062 // continue(v, ...)
1063 //
1064 // If there are no breaks in the body, the break continuation is inlined
1065 // in the exit continuation (i.e., the translation of the successor
1066 // statement occurs in the exit continuation).
1067
1068 // The condition and body are delimited.
1069 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
1070 ir.Primitive condition =
1071 withBuilder(condBuilder, () => visit(node.condition));
1072
1073 JumpTarget target = elements.getTargetDefinition(node);
1074 JumpCollector breakCollector = new JumpCollector(target);
1075 JumpCollector continueCollector = new JumpCollector(target);
1076 irBuilder.state.breakCollectors.add(breakCollector);
1077 irBuilder.state.continueCollectors.add(continueCollector);
1078
1079 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
1080 withBuilder(bodyBuilder, () => visit(node.body));
1081 assert(irBuilder.state.breakCollectors.last == breakCollector);
1082 assert(irBuilder.state.continueCollectors.last == continueCollector);
1083 irBuilder.state.breakCollectors.removeLast();
1084 irBuilder.state.continueCollectors.removeLast();
1085
1086 // Create body entry and loop exit continuations and a branch to them.
1087 ir.Continuation bodyContinuation = new ir.Continuation([]);
1088 ir.Continuation exitContinuation = new ir.Continuation([]);
1089 ir.LetCont branch =
1090 new ir.LetCont(exitContinuation,
1091 new ir.LetCont(bodyContinuation,
1092 new ir.Branch(new ir.IsTrue(condition),
1093 bodyContinuation,
1094 exitContinuation)));
1095 // If there are breaks in the body, then there must be a join-point
1096 // continuation for the normal exit and the breaks.
1097 bool hasBreaks = !breakCollector.isEmpty;
1098 ir.LetCont letJoin;
1099 if (hasBreaks) {
1100 letJoin = new ir.LetCont(null, branch);
1101 condBuilder.add(letJoin);
1102 condBuilder._current = branch;
1103 } else {
1104 condBuilder.add(branch);
1105 }
1106 ir.Continuation loopContinuation =
1107 new ir.Continuation(condBuilder._parameters);
1108 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
1109 invokeFullJoin(loopContinuation, continueCollector, recursive: true);
1110 bodyContinuation.body = bodyBuilder._root;
1111
1112 loopContinuation.body = condBuilder._root;
1113 irBuilder.add(new ir.LetCont(loopContinuation,
1114 new ir.InvokeContinuation(loopContinuation,
1115 irBuilder.environment.index2value)));
1116 if (hasBreaks) {
1117 irBuilder._current = branch;
1118 irBuilder.environment = condBuilder.environment;
1119 breakCollector.addJump(irBuilder);
1120 letJoin.continuation =
1121 createJoin(irBuilder.environment.length, breakCollector);
1122 irBuilder._current = letJoin;
1123 } else {
1124 irBuilder._current = condBuilder._current;
1125 irBuilder.environment = condBuilder.environment;
1126 }
1127 return null;
1128 }
1129
1130 ir.Primitive visitForIn(ast.ForIn node) {
1131 // The for-in loop
1132 //
1133 // for (a in e) s;
1134 //
1135 // Is compiled analogously to:
1136 //
1137 // a = e.iterator;
1138 // while (a.moveNext()) {
1139 // var n0 = a.current;
1140 // s;
1141 // }
1142
1143 // The condition and body are delimited.
1144 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
1145
1146 ir.Primitive expressionReceiver = visit(node.expression);
1147 List<ir.Primitive> emptyArguments = new List<ir.Primitive>();
1148
1149 ir.Parameter iterator = new ir.Parameter(null);
1150 ir.Continuation iteratorInvoked = new ir.Continuation([iterator]);
1151 irBuilder.add(new ir.LetCont(iteratorInvoked,
1152 new ir.InvokeMethod(expressionReceiver,
1153 new Selector.getter("iterator", null), iteratorInvoked,
1154 emptyArguments)));
1155
1156 ir.Parameter condition = new ir.Parameter(null);
1157 ir.Continuation moveNextInvoked = new ir.Continuation([condition]);
1158 condBuilder.add(new ir.LetCont(moveNextInvoked,
1159 new ir.InvokeMethod(iterator,
1160 new Selector.call("moveNext", null, 0),
1161 moveNextInvoked, emptyArguments)));
1162
1163 JumpTarget target = elements.getTargetDefinition(node);
1164 JumpCollector breakCollector = new JumpCollector(target);
1165 JumpCollector continueCollector = new JumpCollector(target);
1166 irBuilder.state.breakCollectors.add(breakCollector);
1167 irBuilder.state.continueCollectors.add(continueCollector);
1168
1169 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
1170 ast.Node identifier = node.declaredIdentifier;
1171 Element variableElement = elements.getForInVariable(node);
1172 Selector selector = elements.getSelector(identifier);
1173
1174 // node.declaredIdentifier can be either an ast.VariableDefinitions
1175 // (defining a new local variable) or a send designating some existing
1176 // variable.
1177 ast.Node declaredIdentifier = node.declaredIdentifier;
1178
1179 if (declaredIdentifier is ast.VariableDefinitions) {
1180 withBuilder(bodyBuilder, () => visit(declaredIdentifier));
1181 }
1182
1183 ir.Parameter currentValue = new ir.Parameter(null);
1184 ir.Continuation currentInvoked = new ir.Continuation([currentValue]);
1185 bodyBuilder.add(new ir.LetCont(currentInvoked,
1186 new ir.InvokeMethod(iterator, new Selector.getter("current", null),
1187 currentInvoked, emptyArguments)));
1188 if (Elements.isLocal(variableElement)) {
1189 withBuilder(bodyBuilder, () => setLocal(variableElement, currentValue));
1190 } else if (Elements.isStaticOrTopLevel(variableElement)) {
1191 withBuilder(bodyBuilder,
1192 () => setStatic(variableElement, selector, currentValue));
1193 } else {
1194 ir.Primitive receiver =
1195 withBuilder(bodyBuilder, () => lookupThis());
1196 withBuilder(bodyBuilder,
1197 () => setDynamic(null, receiver, selector, currentValue));
1198 }
1199
1200 withBuilder(bodyBuilder, () => visit(node.body));
1201 assert(irBuilder.state.breakCollectors.last == breakCollector);
1202 assert(irBuilder.state.continueCollectors.last == continueCollector);
1203 irBuilder.state.breakCollectors.removeLast();
1204 irBuilder.state.continueCollectors.removeLast();
1205
1206 // Create body entry and loop exit continuations and a branch to them.
1207 ir.Continuation bodyContinuation = new ir.Continuation([]);
1208 ir.Continuation exitContinuation = new ir.Continuation([]);
1209 ir.LetCont branch =
1210 new ir.LetCont(exitContinuation,
1211 new ir.LetCont(bodyContinuation,
1212 new ir.Branch(new ir.IsTrue(condition),
1213 bodyContinuation,
1214 exitContinuation)));
1215 // If there are breaks in the body, then there must be a join-point
1216 // continuation for the normal exit and the breaks.
1217 bool hasBreaks = !breakCollector.isEmpty;
1218 ir.LetCont letJoin;
1219 if (hasBreaks) {
1220 letJoin = new ir.LetCont(null, branch);
1221 condBuilder.add(letJoin);
1222 condBuilder._current = branch;
1223 } else {
1224 condBuilder.add(branch);
1225 }
1226 ir.Continuation loopContinuation =
1227 new ir.Continuation(condBuilder._parameters);
1228 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
1229 invokeFullJoin(loopContinuation, continueCollector, recursive: true);
1230 bodyContinuation.body = bodyBuilder._root;
1231
1232 loopContinuation.body = condBuilder._root;
1233 irBuilder.add(new ir.LetCont(loopContinuation,
1234 new ir.InvokeContinuation(loopContinuation,
1235 irBuilder.environment.index2value)));
1236 if (hasBreaks) {
1237 irBuilder._current = branch;
1238 irBuilder.environment = condBuilder.environment;
1239 breakCollector.addJump(irBuilder);
1240 letJoin.continuation =
1241 createJoin(irBuilder.environment.length, breakCollector);
1242 irBuilder._current = letJoin;
1243 } else {
1244 irBuilder._current = condBuilder._current;
1245 irBuilder.environment = condBuilder.environment;
1246 }
1247 return null;
1248 }
1249
1250 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) {
1251 assert(irBuilder.isOpen);
1252 if (node.modifiers.isConst) {
1253 for (ast.SendSet definition in node.definitions.nodes) {
1254 assert(!definition.arguments.isEmpty);
1255 assert(definition.arguments.tail.isEmpty);
1256 VariableElement element = elements[definition];
1257 ConstantExpression value = getConstantForVariable(element);
1258 irBuilder.declareLocalConstant(element, value);
1259 }
1260 } else {
1261 for (ast.Node definition in node.definitions.nodes) {
1262 Element element = elements[definition];
1263 ir.Primitive initialValue;
1264 // Definitions are either SendSets if there is an initializer, or
1265 // Identifiers if there is no initializer.
1266 if (definition is ast.SendSet) {
1267 assert(!definition.arguments.isEmpty);
1268 assert(definition.arguments.tail.isEmpty);
1269 initialValue = visit(definition.arguments.head);
1270 } else {
1271 assert(definition is ast.Identifier);
1272 }
1273 irBuilder.declareLocalVariable(element,
1274 initialValue: initialValue,
1275 isClosureVariable: isClosureVariable(element));
1276 }
1277 }
1278 return null;
1279 }
1280
1281 // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
1282 // where (C', x) = Build(e, C)
1283 //
1284 // Return without a subexpression is translated as if it were return null.
1285 ir.Primitive visitReturn(ast.Return node) {
1286 assert(irBuilder.isOpen);
1287 assert(invariant(node, node.beginToken.value != 'native'));
1288 if (node.expression == null) {
1289 irBuilder.buildReturn();
1290 } else {
1291 irBuilder.buildReturn(visit(node.expression));
1292 }
1293 return null;
1294 }
1295
1296 // ==== Expressions ====
1297 ir.Primitive visitConditional(ast.Conditional node) {
1298 assert(irBuilder.isOpen);
1299 ir.Primitive condition = visit(node.condition);
1300
1301 // The then and else expressions are delimited.
1302 IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder);
1303 IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder);
1304 ir.Primitive thenValue =
1305 withBuilder(thenBuilder, () => visit(node.thenExpression));
1306 ir.Primitive elseValue =
1307 withBuilder(elseBuilder, () => visit(node.elseExpression));
1308
1309 // Treat the values of the subexpressions as named values in the
1310 // environment, so they will be treated as arguments to the join-point
1311 // continuation.
1312 assert(irBuilder.environment.length == thenBuilder.environment.length);
1313 assert(irBuilder.environment.length == elseBuilder.environment.length);
1314 thenBuilder.environment.extend(null, thenValue);
1315 elseBuilder.environment.extend(null, elseValue);
1316 JumpCollector jumps = new JumpCollector(null);
1317 jumps.addJump(thenBuilder);
1318 jumps.addJump(elseBuilder);
1319 ir.Continuation joinContinuation =
1320 createJoin(irBuilder.environment.length + 1, jumps);
1321
1322 // Build the term
1323 // let cont join(x, ..., result) = [] in
1324 // let cont then() = [[thenPart]]; join(v, ...) in
1325 // let cont else() = [[elsePart]]; join(v, ...) in
1326 // if condition (then, else)
1327 ir.Continuation thenContinuation = new ir.Continuation([]);
1328 ir.Continuation elseContinuation = new ir.Continuation([]);
1329 thenContinuation.body = thenBuilder._root;
1330 elseContinuation.body = elseBuilder._root;
1331 irBuilder.add(new ir.LetCont(joinContinuation,
1332 new ir.LetCont(thenContinuation,
1333 new ir.LetCont(elseContinuation,
1334 new ir.Branch(new ir.IsTrue(condition),
1335 thenContinuation,
1336 elseContinuation)))));
1337 return (thenValue == elseValue)
1338 ? thenValue
1339 : joinContinuation.parameters.last;
1340 }
1341
1342 // For all simple literals:
1343 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x]
1344 ir.Primitive visitLiteralBool(ast.LiteralBool node) {
1345 assert(irBuilder.isOpen);
1346 return translateConstant(node);
1347 }
1348
1349 ir.Primitive visitLiteralDouble(ast.LiteralDouble node) {
1350 assert(irBuilder.isOpen);
1351 return translateConstant(node);
1352 }
1353
1354 ir.Primitive visitLiteralInt(ast.LiteralInt node) {
1355 assert(irBuilder.isOpen);
1356 return translateConstant(node);
1357 }
1358
1359 ir.Primitive visitLiteralNull(ast.LiteralNull node) {
1360 assert(irBuilder.isOpen);
1361 return translateConstant(node);
1362 }
1363
1364 ir.Primitive visitLiteralString(ast.LiteralString node) {
1365 assert(irBuilder.isOpen);
1366 return translateConstant(node);
1367 }
1368
1369 ConstantExpression getConstantForNode(ast.Node node) {
1370 ConstantExpression constant =
1371 compiler.backend.constantCompilerTask.compileNode(node, elements);
1372 assert(invariant(node, constant != null,
1373 message: 'No constant computed for $node'));
1374 return constant;
1375 }
1376
1377 ConstantExpression getConstantForVariable(VariableElement element) {
1378 ConstantExpression constant =
1379 compiler.backend.constants.getConstantForVariable(element);
1380 assert(invariant(element, constant != null,
1381 message: 'No constant computed for $element'));
1382 return constant;
1383 }
1384
1385 ir.Primitive visitLiteralList(ast.LiteralList node) {
1386 assert(irBuilder.isOpen);
1387 if (node.isConst) {
1388 return translateConstant(node);
1389 }
1390 List<ir.Primitive> values = node.elements.nodes.mapToList(visit);
1391 GenericType type = elements.getType(node);
1392 ir.Primitive result = new ir.LiteralList(type, values);
1393 irBuilder.add(new ir.LetPrim(result));
1394 return result;
1395 }
1396
1397 ir.Primitive visitLiteralMap(ast.LiteralMap node) {
1398 assert(irBuilder.isOpen);
1399 if (node.isConst) {
1400 return translateConstant(node);
1401 }
1402 List<ir.Primitive> keys = new List<ir.Primitive>();
1403 List<ir.Primitive> values = new List<ir.Primitive>();
1404 node.entries.nodes.forEach((ast.LiteralMapEntry node) {
1405 keys.add(visit(node.key));
1406 values.add(visit(node.value));
1407 });
1408 GenericType type = elements.getType(node);
1409 ir.Primitive result = new ir.LiteralMap(type, keys, values);
1410 irBuilder.add(new ir.LetPrim(result));
1411 return result;
1412 }
1413
1414 ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) {
1415 assert(irBuilder.isOpen);
1416 return translateConstant(node);
1417 }
1418
1419 ir.Primitive visitIdentifier(ast.Identifier node) {
1420 assert(irBuilder.isOpen);
1421 // "this" is the only identifier that should be met by the visitor.
1422 assert(node.isThis());
1423 return lookupThis();
1424 }
1425
1426 ir.Primitive visitParenthesizedExpression(
1427 ast.ParenthesizedExpression node) {
1428 assert(irBuilder.isOpen);
1429 return visit(node.expression);
1430 }
1431
1432 // Stores the result of visiting a CascadeReceiver, so we can return it from
1433 // its enclosing Cascade.
1434 ir.Primitive _currentCascadeReceiver;
1435
1436 ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) {
1437 assert(irBuilder.isOpen);
1438 return _currentCascadeReceiver = visit(node.expression);
1439 }
1440
1441 ir.Primitive visitCascade(ast.Cascade node) {
1442 assert(irBuilder.isOpen);
1443 var oldCascadeReceiver = _currentCascadeReceiver;
1444 // Throw away the result of visiting the expression.
1445 // Instead we return the result of visiting the CascadeReceiver.
1446 this.visit(node.expression);
1447 ir.Primitive receiver = _currentCascadeReceiver;
1448 _currentCascadeReceiver = oldCascadeReceiver;
1449 return receiver;
1450 }
1451
1452 ir.Primitive lookupThis() {
1453 ir.Primitive result = new ir.This();
1454 irBuilder.add(new ir.LetPrim(result));
1455 return result;
1456 }
1457
1458 // ==== Sends ====
1459 ir.Primitive visitAssert(ast.Send node) {
1460 assert(irBuilder.isOpen);
1461 return giveup(node, 'Assert');
1462 }
1463
1464 ir.Primitive visitNamedArgument(ast.NamedArgument node) {
1465 assert(irBuilder.isOpen);
1466 return visit(node.expression);
1467 }
1468
1469 ir.Primitive translateClosureCall(ir.Primitive receiver,
1470 Selector closureSelector,
1471 ast.NodeList arguments) {
1472 Selector namedCallSelector = new Selector(closureSelector.kind,
1473 "call",
1474 closureSelector.library,
1475 closureSelector.argumentCount,
1476 closureSelector.namedArguments);
1477 List<ir.Primitive> args = arguments.nodes.mapToList(visit, growable:false);
1478 return irBuilder.continueWithExpression(
1479 (k) => new ir.InvokeMethod(receiver, namedCallSelector, k, args));
1480 }
1481
1482 ir.Primitive visitClosureSend(ast.Send node) {
1483 assert(irBuilder.isOpen);
1484 Element element = elements[node];
1485 ir.Primitive closureTarget;
1486 if (element == null) {
1487 closureTarget = visit(node.selector);
1488 } else if (isClosureVariable(element)) {
1489 LocalElement local = element;
1490 closureTarget = new ir.GetClosureVariable(local);
1491 irBuilder.add(new ir.LetPrim(closureTarget));
1492 } else {
1493 assert(Elements.isLocal(element));
1494 closureTarget = irBuilder.environment.lookup(element);
1495 }
1496 Selector closureSelector = elements.getSelector(node);
1497 return translateClosureCall(closureTarget, closureSelector,
1498 node.argumentsNode);
1499 }
1500
1501 /// If [node] is null, returns this.
1502 /// If [node] is super, returns null (for special handling)
1503 /// Otherwise visits [node] and returns the result.
1504 ir.Primitive visitReceiver(ast.Expression node) {
1505 if (node == null) return lookupThis();
1506 if (node.isSuper()) return null;
1507 return visit(node);
1508 }
1509
1510 /// Makes an [InvokeMethod] unless [node.receiver.isSuper()], in that case
1511 /// makes an [InvokeSuperMethod] ignoring [receiver].
1512 ir.Expression createDynamicInvoke(ast.Send node,
1513 Selector selector,
1514 ir.Definition receiver,
1515 ir.Continuation k,
1516 List<ir.Definition> arguments) {
1517 return node != null && node.receiver != null && node.receiver.isSuper()
1518 ? new ir.InvokeSuperMethod(selector, k, arguments)
1519 : new ir.InvokeMethod(receiver, selector, k, arguments);
1520 }
1521
1522 ir.Primitive visitDynamicSend(ast.Send node) {
1523 assert(irBuilder.isOpen);
1524 Selector selector = elements.getSelector(node);
1525 ir.Primitive receiver = visitReceiver(node.receiver);
1526 List<ir.Primitive> arguments = new List<ir.Primitive>();
1527 for (ast.Node n in node.arguments) {
1528 arguments.add(visit(n));
1529 }
1530 return irBuilder.buildDynamicInvocation(receiver, selector, arguments);
1531 }
1532
1533 _GetterElements translateGetter(ast.Send node, Selector selector) {
1534 Element element = elements[node];
1535 ir.Primitive result;
1536 ir.Primitive receiver;
1537 ir.Primitive index;
1538
1539 if (element != null && element.isConst) {
1540 // Reference to constant local, top-level or static field
1541 result = translateConstant(node);
1542 } else if (isClosureVariable(element)) {
1543 LocalElement local = element;
1544 result = new ir.GetClosureVariable(local);
1545 irBuilder.add(new ir.LetPrim(result));
1546 } else if (Elements.isLocal(element)) {
1547 // Reference to local variable
1548 result = irBuilder.buildLocalGet(element);
1549 } else if (element == null ||
1550 Elements.isInstanceField(element) ||
1551 Elements.isInstanceMethod(element) ||
1552 selector.isIndex ||
1553 // TODO(johnniwinther): clean up semantics of resolution.
1554 node.isSuperCall) {
1555 // Dynamic dispatch to a getter. Sometimes resolution will suggest a
1556 // target element, but in these cases we must still emit a dynamic
1557 // dispatch. The target element may be an instance method in case we are
1558 // converting a method to a function object.
1559
1560 receiver = visitReceiver(node.receiver);
1561 List<ir.Primitive> arguments = new List<ir.Primitive>();
1562 if (selector.isIndex) {
1563 index = visit(node.arguments.head);
1564 arguments.add(index);
1565 }
1566
1567 assert(selector.kind == SelectorKind.GETTER ||
1568 selector.kind == SelectorKind.INDEX);
1569 result = irBuilder.continueWithExpression(
1570 (k) => createDynamicInvoke(node, selector, receiver, k, arguments));
1571 } else if (element.isField || element.isGetter || element.isErroneous ||
1572 element.isSetter) {
1573 // TODO(johnniwinther): Change handling of setter selectors.
1574 // Access to a static field or getter (non-static case handled above).
1575 // Even if there is only a setter, we compile as if it was a getter,
1576 // so the vm can fail at runtime.
1577 assert(selector.kind == SelectorKind.GETTER ||
1578 selector.kind == SelectorKind.SETTER);
1579 result = irBuilder.buildStaticGet(element, selector);
1580 } else if (Elements.isStaticOrTopLevelFunction(element)) {
1581 // Convert a top-level or static function to a function object.
1582 result = translateConstant(node);
1583 } else {
1584 throw "Unexpected SendSet getter: $node, $element";
1585 }
1586 return new _GetterElements(
1587 result: result,index: index, receiver: receiver);
1588 }
1589
1590 ir.Primitive visitGetterSend(ast.Send node) {
1591 assert(irBuilder.isOpen);
1592 return translateGetter(node, elements.getSelector(node)).result;
1593
1594 }
1595
1596 ir.Primitive buildNegation(ir.Primitive condition) {
1597 // ! e is translated as e ? false : true
1598
1599 // Add a continuation parameter for the result of the expression.
1600 ir.Parameter resultParameter = new ir.Parameter(null);
1601
1602 ir.Continuation joinContinuation = new ir.Continuation([resultParameter]);
1603 ir.Continuation thenContinuation = new ir.Continuation([]);
1604 ir.Continuation elseContinuation = new ir.Continuation([]);
1605
1606 ir.Constant trueConstant = irBuilder.makePrimConst(
1607 irBuilder.state.constantSystem.createBool(true));
1608 ir.Constant falseConstant = irBuilder.makePrimConst(
1609 irBuilder.state.constantSystem.createBool(false));
1610
1611 thenContinuation.body = new ir.LetPrim(falseConstant)
1612 ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant]));
1613 elseContinuation.body = new ir.LetPrim(trueConstant)
1614 ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant]));
1615
1616 irBuilder.add(new ir.LetCont(joinContinuation,
1617 new ir.LetCont(thenContinuation,
1618 new ir.LetCont(elseContinuation,
1619 new ir.Branch(new ir.IsTrue(condition),
1620 thenContinuation,
1621 elseContinuation)))));
1622 return resultParameter;
1623 }
1624
1625 ir.Primitive translateLogicalOperator(ast.Operator op,
1626 ast.Expression left,
1627 ast.Expression right) {
1628 // e0 && e1 is translated as if e0 ? (e1 == true) : false.
1629 // e0 || e1 is translated as if e0 ? true : (e1 == true).
1630 // The translation must convert both e0 and e1 to booleans and handle
1631 // local variable assignments in e1.
1632
1633 ir.Primitive leftValue = visit(left);
1634 IrBuilder rightBuilder = new IrBuilder.delimited(irBuilder);
1635 ir.Primitive rightValue =
1636 withBuilder(rightBuilder, () => visit(right));
1637 // A dummy empty target for the branch on the left subexpression branch.
1638 // This enables using the same infrastructure for join-point continuations
1639 // as in visitIf and visitConditional. It will hold a definition of the
1640 // appropriate constant and an invocation of the join-point continuation.
1641 IrBuilder emptyBuilder = new IrBuilder.delimited(irBuilder);
1642 // Dummy empty targets for right true and right false. They hold
1643 // definitions of the appropriate constant and an invocation of the
1644 // join-point continuation.
1645 IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder);
1646 IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder);
1647
1648 // If we don't evaluate the right subexpression, the value of the whole
1649 // expression is this constant.
1650 ir.Constant leftBool = emptyBuilder.makePrimConst(
1651 emptyBuilder.state.constantSystem.createBool(op.source == '||'));
1652 // If we do evaluate the right subexpression, the value of the expression
1653 // is a true or false constant.
1654 ir.Constant rightTrue = rightTrueBuilder.makePrimConst(
1655 rightTrueBuilder.state.constantSystem.createBool(true));
1656 ir.Constant rightFalse = rightFalseBuilder.makePrimConst(
1657 rightFalseBuilder.state.constantSystem.createBool(false));
1658 emptyBuilder.add(new ir.LetPrim(leftBool));
1659 rightTrueBuilder.add(new ir.LetPrim(rightTrue));
1660 rightFalseBuilder.add(new ir.LetPrim(rightFalse));
1661
1662 // Treat the result values as named values in the environment, so they
1663 // will be treated as arguments to the join-point continuation.
1664 assert(irBuilder.environment.length == emptyBuilder.environment.length);
1665 assert(irBuilder.environment.length == rightTrueBuilder.environment.length);
1666 assert(irBuilder.environment.length ==
1667 rightFalseBuilder.environment.length);
1668 emptyBuilder.environment.extend(null, leftBool);
1669 rightTrueBuilder.environment.extend(null, rightTrue);
1670 rightFalseBuilder.environment.extend(null, rightFalse);
1671
1672 // Wire up two continuations for the left subexpression, two continuations
1673 // for the right subexpression, and a three-way join continuation.
1674 JumpCollector jumps = new JumpCollector(null);
1675 jumps.addJump(emptyBuilder);
1676 jumps.addJump(rightTrueBuilder);
1677 jumps.addJump(rightFalseBuilder);
1678 ir.Continuation joinContinuation =
1679 createJoin(irBuilder.environment.length + 1, jumps);
1680 ir.Continuation leftTrueContinuation = new ir.Continuation([]);
1681 ir.Continuation leftFalseContinuation = new ir.Continuation([]);
1682 ir.Continuation rightTrueContinuation = new ir.Continuation([]);
1683 ir.Continuation rightFalseContinuation = new ir.Continuation([]);
1684 rightTrueContinuation.body = rightTrueBuilder._root;
1685 rightFalseContinuation.body = rightFalseBuilder._root;
1686 // The right subexpression has two continuations.
1687 rightBuilder.add(
1688 new ir.LetCont(rightTrueContinuation,
1689 new ir.LetCont(rightFalseContinuation,
1690 new ir.Branch(new ir.IsTrue(rightValue),
1691 rightTrueContinuation,
1692 rightFalseContinuation))));
1693 // Depending on the operator, the left subexpression's continuations are
1694 // either the right subexpression or an invocation of the join-point
1695 // continuation.
1696 if (op.source == '&&') {
1697 leftTrueContinuation.body = rightBuilder._root;
1698 leftFalseContinuation.body = emptyBuilder._root;
1699 } else {
1700 leftTrueContinuation.body = emptyBuilder._root;
1701 leftFalseContinuation.body = rightBuilder._root;
1702 }
1703
1704 irBuilder.add(new ir.LetCont(joinContinuation,
1705 new ir.LetCont(leftTrueContinuation,
1706 new ir.LetCont(leftFalseContinuation,
1707 new ir.Branch(new ir.IsTrue(leftValue),
1708 leftTrueContinuation,
1709 leftFalseContinuation)))));
1710 // There is always a join parameter for the result value, because it
1711 // is different on at least two paths.
1712 return joinContinuation.parameters.last;
1713 }
1714
1715 ir.Primitive visitOperatorSend(ast.Send node) {
1716 assert(irBuilder.isOpen);
1717 ast.Operator op = node.selector;
1718 if (isUserDefinableOperator(op.source)) {
1719 return visitDynamicSend(node);
1720 }
1721 if (op.source == '&&' || op.source == '||') {
1722 assert(node.receiver != null);
1723 assert(!node.arguments.isEmpty);
1724 assert(node.arguments.tail.isEmpty);
1725 return translateLogicalOperator(op, node.receiver, node.arguments.head);
1726 }
1727 if (op.source == "!") {
1728 assert(node.receiver != null);
1729 assert(node.arguments.isEmpty);
1730 return buildNegation(visit(node.receiver));
1731 }
1732 if (op.source == "!=") {
1733 assert(node.receiver != null);
1734 assert(!node.arguments.isEmpty);
1735 assert(node.arguments.tail.isEmpty);
1736 return buildNegation(visitDynamicSend(node));
1737 }
1738 assert(invariant(node, op.source == "is" || op.source == "as",
1739 message: "unexpected operator $op"));
1740 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
1741 ir.Primitive receiver = visit(node.receiver);
1742 ir.Primitive check = irBuilder.continueWithExpression(
1743 (k) => new ir.TypeOperator(op.source, receiver, type, k));
1744 return node.isIsNotCheck ? buildNegation(check) : check;
1745 }
1746
1747 // Build(StaticSend(f, arguments), C) = C[C'[InvokeStatic(f, xs)]]
1748 // where (C', xs) = arguments.fold(Build, C)
1749 ir.Primitive visitStaticSend(ast.Send node) {
1750 assert(irBuilder.isOpen);
1751 Element element = elements[node];
1752 assert(!element.isConstructor);
1753 // TODO(lry): support foreign functions.
1754 if (element.isForeign(compiler.backend)) {
1755 return giveup(node, 'StaticSend: foreign');
1756 }
1757
1758 Selector selector = elements.getSelector(node);
1759
1760 // TODO(lry): support default arguments, need support for locals.
1761 List<ir.Definition> arguments = node.arguments.mapToList(visit,
1762 growable:false);
1763 return irBuilder.buildStaticInvocation(element, selector, arguments);
1764 }
1765
1766
1767 ir.Primitive visitSuperSend(ast.Send node) {
1768 assert(irBuilder.isOpen);
1769 if (node.isPropertyAccess) {
1770 return visitGetterSend(node);
1771 } else {
1772 Selector selector = elements.getSelector(node);
1773 List<ir.Primitive> arguments = new List<ir.Primitive>();
1774 for (ast.Node n in node.arguments) {
1775 arguments.add(visit(n));
1776 }
1777 return irBuilder.buildSuperInvocation(selector, arguments);
1778 }
1779 }
1780
1781 visitTypePrefixSend(ast.Send node) {
1782 compiler.internalError(node, "visitTypePrefixSend should not be called.");
1783 }
1784
1785 ir.Primitive visitTypeLiteralSend(ast.Send node) {
1786 assert(irBuilder.isOpen);
1787 // If the user is trying to invoke the type literal or variable,
1788 // it must be treated as a function call.
1789 if (node.argumentsNode != null) {
1790 // TODO(sigurdm): Handle this to match proposed semantics of issue #19725.
1791 return giveup(node, 'Type literal invoked as function');
1792 }
1793
1794 DartType type = elements.getTypeLiteralType(node);
1795 if (type is TypeVariableType) {
1796 ir.Primitive prim = new ir.ReifyTypeVar(type.element);
1797 irBuilder.add(new ir.LetPrim(prim));
1798 return prim;
1799 } else {
1800 return translateConstant(node);
1801 }
1802 }
1803
1804 /// True if [element] is a local variable, local function, or parameter that
1805 /// is accessed from an inner function. Recursive self-references in a local
1806 /// function count as closure accesses.
1807 ///
1808 /// If `true`, [element] is a [LocalElement].
1809 bool isClosureVariable(Element element) {
1810 return irBuilder.state.closureLocals.contains(element);
1811 }
1812
1813 void setLocal(Element element, ir.Primitive valueToStore) {
1814 if (isClosureVariable(element)) {
1815 LocalElement local = element;
1816 irBuilder.add(new ir.SetClosureVariable(local, valueToStore));
1817 } else {
1818 valueToStore.useElementAsHint(element);
1819 irBuilder.environment.update(element, valueToStore);
1820 }
1821 }
1822
1823 void setStatic(Element element,
1824 Selector selector,
1825 ir.Primitive valueToStore) {
1826 assert(element.isErroneous || element.isField || element.isSetter);
1827 irBuilder.continueWithExpression(
1828 (k) => new ir.InvokeStatic(element, selector, k, [valueToStore]));
1829 }
1830
1831 void setDynamic(ast.Node node,
1832 ir.Primitive receiver, Selector selector,
1833 ir.Primitive valueToStore) {
1834 List<ir.Definition> arguments = [valueToStore];
1835 irBuilder.continueWithExpression(
1836 (k) => createDynamicInvoke(node, selector, receiver, k, arguments));
1837 }
1838
1839 void setIndex(ast.Node node,
1840 ir.Primitive receiver,
1841 Selector selector,
1842 ir.Primitive index,
1843 ir.Primitive valueToStore) {
1844 List<ir.Definition> arguments = [index, valueToStore];
1845 irBuilder.continueWithExpression(
1846 (k) => createDynamicInvoke(node, selector, receiver, k, arguments));
1847 }
1848
1849 ir.Primitive visitSendSet(ast.SendSet node) {
1850 assert(irBuilder.isOpen);
1851 Element element = elements[node];
1852 ast.Operator op = node.assignmentOperator;
1853 // For complex operators, this is the result of getting (before assigning)
1854 ir.Primitive originalValue;
1855 // For []+= style operators, this saves the index.
1856 ir.Primitive index;
1857 ir.Primitive receiver;
1858 // This is what gets assigned.
1859 ir.Primitive valueToStore;
1860 Selector selector = elements.getSelector(node);
1861 Selector operatorSelector =
1862 elements.getOperatorSelectorInComplexSendSet(node);
1863 Selector getterSelector =
1864 elements.getGetterSelectorInComplexSendSet(node);
1865 assert(
1866 // Indexing send-sets have an argument for the index.
1867 (selector.isIndexSet ? 1 : 0) +
1868 // Non-increment send-sets have one more argument.
1869 (ast.Operator.INCREMENT_OPERATORS.contains(op.source) ? 0 : 1)
1870 == node.argumentCount());
1871
1872 ast.Node getAssignArgument() {
1873 assert(invariant(node, !node.arguments.isEmpty,
1874 message: "argument expected"));
1875 return selector.isIndexSet
1876 ? node.arguments.tail.head
1877 : node.arguments.head;
1878 }
1879
1880 // Get the value into valueToStore
1881 if (op.source == "=") {
1882 if (selector.isIndexSet) {
1883 receiver = visitReceiver(node.receiver);
1884 index = visit(node.arguments.head);
1885 } else if (element == null || Elements.isInstanceField(element)) {
1886 receiver = visitReceiver(node.receiver);
1887 }
1888 valueToStore = visit(getAssignArgument());
1889 } else {
1890 // Get the original value into getter
1891 assert(ast.Operator.COMPLEX_OPERATORS.contains(op.source));
1892
1893 _GetterElements getterResult = translateGetter(node, getterSelector);
1894 index = getterResult.index;
1895 receiver = getterResult.receiver;
1896 originalValue = getterResult.result;
1897
1898 // Do the modification of the value in getter.
1899 ir.Primitive arg;
1900 if (ast.Operator.INCREMENT_OPERATORS.contains(op.source)) {
1901 arg = irBuilder.makePrimConst(
1902 irBuilder.state.constantSystem.createInt(1));
1903 irBuilder.add(new ir.LetPrim(arg));
1904 } else {
1905 arg = visit(getAssignArgument());
1906 }
1907 valueToStore = new ir.Parameter(null);
1908 ir.Continuation k = new ir.Continuation([valueToStore]);
1909 ir.Expression invoke =
1910 new ir.InvokeMethod(originalValue, operatorSelector, k, [arg]);
1911 irBuilder.add(new ir.LetCont(k, invoke));
1912 }
1913
1914 if (Elements.isLocal(element)) {
1915 setLocal(element, valueToStore);
1916 } else if ((!node.isSuperCall && Elements.isErroneousElement(element)) ||
1917 Elements.isStaticOrTopLevel(element)) {
1918 setStatic(element, elements.getSelector(node), valueToStore);
1919 } else {
1920 // Setter or index-setter invocation
1921 Selector selector = elements.getSelector(node);
1922 assert(selector.kind == SelectorKind.SETTER ||
1923 selector.kind == SelectorKind.INDEX);
1924 if (selector.isIndexSet) {
1925 setIndex(node, receiver, selector, index, valueToStore);
1926 } else {
1927 setDynamic(node, receiver, selector, valueToStore);
1928 }
1929 }
1930
1931 if (node.isPostfix) {
1932 assert(originalValue != null);
1933 return originalValue;
1934 } else {
1935 return valueToStore;
1936 }
1937 }
1938
1939 ir.Primitive visitNewExpression(ast.NewExpression node) {
1940 assert(irBuilder.isOpen);
1941 if (node.isConst) {
1942 return translateConstant(node);
1943 }
1944 FunctionElement element = elements[node.send];
1945 Selector selector = elements.getSelector(node.send);
1946 ast.Node selectorNode = node.send.selector;
1947 DartType type = elements.getType(node);
1948 List<ir.Primitive> args =
1949 node.send.arguments.mapToList(visit, growable:false);
1950 return irBuilder.continueWithExpression(
1951 (k) => new ir.InvokeConstructor(type, element,selector, k, args));
1952 }
1953
1954 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) {
1955 assert(irBuilder.isOpen);
1956 ir.Primitive first = visit(node.first);
1957 ir.Primitive second = visit(node.second);
1958 return irBuilder.continueWithExpression(
1959 (k) => new ir.ConcatenateStrings(k, [first, second]));
1960 }
1961
1962 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) {
1963 assert(irBuilder.isOpen);
1964 List<ir.Primitive> arguments = [];
1965 arguments.add(visitLiteralString(node.string));
1966 var it = node.parts.iterator;
1967 while (it.moveNext()) {
1968 ast.StringInterpolationPart part = it.current;
1969 arguments.add(visit(part.expression));
1970 arguments.add(visitLiteralString(part.string));
1971 }
1972 return irBuilder.continueWithExpression(
1973 (k) => new ir.ConcatenateStrings(k, arguments));
1974 }
1975
1976 ir.Primitive translateConstant(ast.Node node, [ConstantExpression constant]) {
1977 assert(irBuilder.isOpen);
1978 if (constant == null) {
1979 constant = getConstantForNode(node);
1980 }
1981 ir.Primitive primitive = irBuilder.makeConst(constant);
1982 irBuilder.add(new ir.LetPrim(primitive));
1983 return primitive;
1984 }
1985
1986 ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) {
1987 // TODO(johnniwinther): Share the visitor.
1988 return new IrBuilderVisitor(elements, compiler, sourceFile)
1989 .buildFunctionInternal(elements[node]);
1990 }
1991
1992 ir.Primitive visitFunctionExpression(ast.FunctionExpression node) {
1993 FunctionElement element = elements[node];
1994 ir.FunctionDefinition inner = makeSubFunction(node);
1995 ir.CreateFunction prim = new ir.CreateFunction(inner);
1996 irBuilder.add(new ir.LetPrim(prim));
1997 return prim;
1998 }
1999
2000 ir.Primitive visitFunctionDeclaration(ast.FunctionDeclaration node) {
2001 LocalFunctionElement element = elements[node.function];
2002 ir.FunctionDefinition inner = makeSubFunction(node.function);
2003 if (isClosureVariable(element)) {
2004 irBuilder.add(new ir.DeclareFunction(element, inner));
2005 } else {
2006 ir.CreateFunction prim = new ir.CreateFunction(inner);
2007 irBuilder.add(new ir.LetPrim(prim));
2008 irBuilder.environment.extend(element, prim);
2009 prim.useElementAsHint(element);
2010 }
2011 return null;
2012 }
2013
2014 static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
2015
2016 dynamic giveup(ast.Node node, [String reason]) {
2017 throw ABORT_IRNODE_BUILDER;
2018 }
2019
2020 ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
2021 try {
2022 return action();
2023 } catch(e, tr) {
2024 if (e == ABORT_IRNODE_BUILDER) {
2025 return null;
2026 }
2027 rethrow;
2028 }
2029 }
2030
2031 void internalError(String reason, {ast.Node node}) {
2032 giveup(node);
2033 }
2034 }
2035
2036 /// Classifies local variables and local functions as 'closure variables'.
2037 /// A closure variable is one that is accessed from an inner function nested
2038 /// one or more levels inside the one that declares it.
2039 class DetectClosureVariables extends ast.Visitor {
2040 final TreeElements elements;
2041 DetectClosureVariables(this.elements);
2042
2043 FunctionElement currentFunction;
2044 Set<Local> usedFromClosure = new Set<Local>();
2045 Set<FunctionElement> recursiveFunctions = new Set<FunctionElement>();
2046
2047 bool isClosureVariable(Entity entity) => usedFromClosure.contains(entity);
2048
2049 void markAsClosureVariable(Local local) {
2050 usedFromClosure.add(local);
2051 }
2052
2053 visit(ast.Node node) => node.accept(this);
2054
2055 visitNode(ast.Node node) {
2056 node.visitChildren(this);
2057 }
2058
2059 visitSend(ast.Send node) {
2060 Element element = elements[node];
2061 if (Elements.isLocal(element) &&
2062 !element.isConst &&
2063 element.enclosingElement != currentFunction) {
2064 LocalElement local = element;
2065 markAsClosureVariable(local);
2066 }
2067 node.visitChildren(this);
2068 }
2069
2070 visitFunctionExpression(ast.FunctionExpression node) {
2071 FunctionElement oldFunction = currentFunction;
2072 currentFunction = elements[node];
2073 visit(node.body);
2074 currentFunction = oldFunction;
2075 }
2076
2077 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698