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

Unified Diff: pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
deleted file mode 100644
index cc2786eb74a318e3997d60e9b2ab0dfcb4f259b7..0000000000000000000000000000000000000000
--- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
+++ /dev/null
@@ -1,1391 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library tree_ir.optimization.statement_rewriter;
-
-import '../../elements/elements.dart';
-import '../../io/source_information.dart';
-import '../../js/placeholder_safety.dart';
-import '../tree_ir_nodes.dart';
-import 'optimization.dart' show Pass;
-
-/**
- * Translates to direct-style.
- *
- * In addition to the general IR constraints (see [CheckTreeIntegrity]),
- * the input is assumed to satisfy the following criteria:
- *
- * All expressions other than those nested in [Assign] or [ExpressionStatement]
- * must be simple. A [VariableUse] and [This] is a simple expression.
- * The right-hand of an [Assign] may not be an [Assign].
- *
- * Moreover, every variable must either be an SSA variable or a mutable
- * variable, and must satisfy the corresponding criteria:
- *
- * SSA VARIABLE:
- * An SSA variable must have a unique definition site, which is either an
- * assignment or label. In case of a label, its target must act as the unique
- * reaching definition of that variable at all uses of the variable and at
- * all other label targets where the variable is in scope.
- *
- * (The second criterion is to ensure that we can move a use of an SSA variable
- * across a label without changing its reaching definition).
- *
- * MUTABLE VARIABLE:
- * Uses of mutable variables are considered complex expressions, and hence must
- * not be nested in other expressions. Assignments to mutable variables must
- * have simple right-hand sides.
- *
- * ----
- *
- * This pass performs the following transformations on the tree:
- * - Assignment inlining
- * - Assignment expression propagation
- * - If-to-conditional conversion
- * - Flatten nested ifs
- * - Break inlining
- * - Redirect breaks
- *
- * The above transformations all eliminate statements from the tree, and may
- * introduce redexes of each other.
- *
- *
- * ASSIGNMENT INLINING:
- * Single-use definitions are inlined at their use site when possible.
- * For example:
- *
- * { v0 = foo(); return v0; }
- * ==>
- * return foo()
- *
- * After translating out of CPS, all intermediate values are bound by [Assign].
- * This transformation propagates such definitions to their uses when it is
- * safe and profitable. Bindings are processed "on demand" when their uses are
- * seen, but are only processed once to keep this transformation linear in
- * the size of the tree.
- *
- * The transformation builds an environment containing [Assign] bindings that
- * are in scope. These bindings have yet-untranslated definitions. When a use
- * is encountered the transformation determines if it is safe and profitable
- * to propagate the definition to its use. If so, it is removed from the
- * environment and the definition is recursively processed (in the
- * new environment at the use site) before being propagated.
- *
- * See [visitVariableUse] for the implementation of the heuristic for
- * propagating a definition.
- *
- *
- * ASSIGNMENT EXPRESSION PROPAGATION:
- * Definitions with multiple uses are propagated to their first use site
- * when possible. For example:
- *
- * { v0 = foo(); bar(v0); return v0; }
- * ==>
- * { bar(v0 = foo()); return v0; }
- *
- * Note that the [RestoreInitializers] phase will later undo this rewrite
- * in cases where it prevents an assignment from being pulled into an
- * initializer.
- *
- *
- * IF-TO-CONDITIONAL CONVERSION:
- * If-statement are converted to conditional expressions when possible.
- * For example:
- *
- * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L }
- * ==>
- * { v1 = v0 ? foo() : bar(); break L }
- *
- * This can lead to inlining of L, which in turn can lead to further propagation
- * of the variable v1.
- *
- * See [visitIf].
- *
- *
- * FLATTEN NESTED IFS:
- * An if inside an if is converted to an if with a logical operator.
- * For example:
- *
- * if (E1) { if (E2) {S} else break L } else break L
- * ==>
- * if (E1 && E2) {S} else break L
- *
- * This may lead to inlining of L.
- *
- *
- * BREAK INLINING:
- * Single-use labels are inlined at [Break] statements.
- * For example:
- *
- * L0: { v0 = foo(); break L0 }; return v0;
- * ==>
- * v0 = foo(); return v0;
- *
- * This can lead to propagation of v0.
- *
- * See [visitBreak] and [visitLabeledStatement].
- *
- *
- * REDIRECT BREAKS:
- * Labeled statements whose next is a break become flattened and all breaks
- * to their label are redirected.
- * For example, where 'jump' is either break or continue:
- *
- * L0: {... break L0 ...}; jump L1
- * ==>
- * {... jump L1 ...}
- *
- * This may trigger a flattening of nested ifs in case the eliminated label
- * separated two ifs.
- */
-class StatementRewriter extends Transformer implements Pass {
- String get passName => 'Statement rewriter';
-
- @override
- void rewrite(FunctionDefinition node) {
- node.parameters.forEach(pushDominatingAssignment);
- node.body = visitStatement(node.body);
- node.parameters.forEach(popDominatingAssignment);
- }
-
- /// The most recently evaluated impure expressions, with the most recent
- /// expression being last.
- ///
- /// Most importantly, this contains [Assign] expressions that we attempt to
- /// inline at their use site. It also contains other impure expressions that
- /// we can propagate to a variable use if they are known to return the value
- /// of that variable.
- ///
- /// Assignments with constant right-hand sides (see [isEffectivelyConstant])
- /// are not considered impure and are put in [constantEnvironment] instead.
- ///
- /// Except for [Conditional]s, expressions in the environment have
- /// not been processed, and all their subexpressions must therefore be
- /// variables uses.
- List<Expression> environment = <Expression>[];
-
- /// Binding environment for variables that are assigned to effectively
- /// constant expressions (see [isEffectivelyConstant]).
- Map<Variable, Expression> constantEnvironment = <Variable, Expression>{};
-
- /// Substitution map for labels. Any break to a label L should be substituted
- /// for a break to L' if L maps to L'.
- Map<Label, Jump> labelRedirects = <Label, Jump>{};
-
- /// Number of uses of the given variable that are still unseen.
- /// Used to detect the first use of a variable (since we do backwards
- /// traversal, the first use is the last one seen).
- Map<Variable, int> unseenUses = <Variable, int>{};
-
- /// Number of assignments to a given variable that dominate the current
- /// position.
- ///
- /// Pure expressions will not be inlined if it uses a variable with more than
- /// one dominating assignment, because the reaching definition of the used
- /// variable might have changed since it was put in the environment.
- final Map<Variable, int> dominatingAssignments = <Variable, int>{};
-
- /// A set of labels that can be safely inlined at their use.
- ///
- /// The successor statements for labeled statements that have only one break
- /// from them are normally rewritten inline at the site of the break. This
- /// is not safe if the code would be moved inside the scope of an exception
- /// handler (i.e., if the code would be moved into a try from outside it).
- Set<Label> safeForInlining = new Set<Label>();
-
- /// If the top element is true, assignments of form "x = CONST" may be
- /// propagated into a following occurence of CONST. This may confuse the JS
- /// engine so it is disabled in some cases.
- final List<bool> allowRhsPropagation = <bool>[true];
-
- bool get isRhsPropagationAllowed => allowRhsPropagation.last;
-
- /// Returns the redirect target of [jump] or [jump] itself if it should not
- /// be redirected.
- Jump redirect(Jump jump) {
- Jump newJump = labelRedirects[jump.target];
- return newJump != null ? newJump : jump;
- }
-
- void inEmptyEnvironment(void action(), {bool keepConstants: true}) {
- List oldEnvironment = environment;
- Map oldConstantEnvironment = constantEnvironment;
- environment = <Expression>[];
- if (!keepConstants) {
- constantEnvironment = <Variable, Expression>{};
- }
- action();
- assert(environment.isEmpty);
- environment = oldEnvironment;
- if (!keepConstants) {
- constantEnvironment = oldConstantEnvironment;
- }
- }
-
- /// Left-hand side of the given assignment, or `null` if not an assignment.
- Variable getLeftHand(Expression e) {
- return e is Assign ? e.variable : null;
- }
-
- /// If the given expression always returns the value of one of its
- /// subexpressions, returns that subexpression, otherwise `null`.
- Expression getValueSubexpression(Expression e) {
- if (e is SetField) return e.value;
- return null;
- }
-
- /// If the given expression always returns the value of one of its
- /// subexpressions, and that subexpression is a variable use, returns that
- /// variable. Otherwise `null`.
- Variable getRightHandVariable(Expression e) {
- Expression value = getValueSubexpression(e);
- return value is VariableUse ? value.variable : null;
- }
-
- Constant getRightHandConstant(Expression e) {
- Expression value = getValueSubexpression(e);
- return value is Constant ? value : null;
- }
-
- /// True if the given expression (taken from [constantEnvironment]) uses a
- /// variable that might have been reassigned since [node] was evaluated.
- bool hasUnsafeVariableUse(Expression node) {
- bool wasFound = false;
- VariableUseVisitor.visit(node, (VariableUse use) {
- if (dominatingAssignments[use.variable] > 1) {
- wasFound = true;
- }
- });
- return wasFound;
- }
-
- void pushDominatingAssignment(Variable variable) {
- if (variable != null) {
- dominatingAssignments.putIfAbsent(variable, () => 0);
- ++dominatingAssignments[variable];
- }
- }
-
- void popDominatingAssignment(Variable variable) {
- if (variable != null) {
- --dominatingAssignments[variable];
- }
- }
-
- @override
- Expression visitVariableUse(VariableUse node) {
- // Count of number of unseen uses remaining.
- unseenUses.putIfAbsent(node.variable, () => node.variable.readCount);
- --unseenUses[node.variable];
-
- // We traverse the tree right-to-left, so when we have seen all uses,
- // it means we are looking at the first use.
- assert(unseenUses[node.variable] < node.variable.readCount);
- assert(unseenUses[node.variable] >= 0);
-
- // We cannot reliably find the first dynamic use of a variable that is
- // accessed from a JS function in a foreign code fragment.
- if (node.variable.isCaptured) return node;
-
- bool isFirstUse = unseenUses[node.variable] == 0;
-
- // Propagate constant to use site.
- Expression constant = constantEnvironment[node.variable];
- if (constant != null && !hasUnsafeVariableUse(constant)) {
- --node.variable.readCount;
- return visitExpression(constant);
- }
-
- // Try to propagate another expression into this variable use.
- if (!environment.isEmpty) {
- Expression binding = environment.last;
-
- // Is this variable assigned by the most recently evaluated impure
- // expression?
- //
- // If so, propagate the assignment, e.g:
- //
- // { x = foo(); bar(x, x) } ==> bar(x = foo(), x)
- //
- // We must ensure that no other uses separate this use from the
- // assignment. We therefore only propagate assignments into the first use.
- //
- // Note that if this is only use, `visitAssign` will then remove the
- // redundant assignment.
- if (getLeftHand(binding) == node.variable && isFirstUse) {
- environment.removeLast();
- --node.variable.readCount;
- return visitExpression(binding);
- }
-
- // Is the most recently evaluated impure expression known to have the
- // value of this variable?
- //
- // If so, we can replace this use with the impure expression, e.g:
- //
- // { E.foo = x; bar(x) } ==> bar(E.foo = x)
- //
- if (isRhsPropagationAllowed &&
- getRightHandVariable(binding) == node.variable) {
- environment.removeLast();
- --node.variable.readCount;
- return visitExpression(binding);
- }
- }
-
- // If the definition could not be propagated, leave the variable use.
- return node;
- }
-
- /// True if [exp] contains a use of a variable that was assigned to by the
- /// most recently evaluated impure expression (constant assignments are not
- /// considered impure).
- ///
- /// This implies that the assignment can be propagated into this use unless
- /// the use is moved further away.
- ///
- /// In this case, we will refrain from moving [exp] across other impure
- /// expressions, even when this is safe, because doing so would immediately
- /// prevent the previous expression from propagating, canceling out the
- /// benefit we might otherwise gain from propagating [exp].
- ///
- /// [exp] must be an unprocessed expression, i.e. either a [Conditional] or
- /// an expression whose subexpressions are all variable uses.
- bool usesRecentlyAssignedVariable(Expression exp) {
- if (environment.isEmpty) return false;
- Variable variable = getLeftHand(environment.last);
- if (variable == null) return false;
- IsVariableUsedVisitor visitor = new IsVariableUsedVisitor(variable);
- visitor.visitExpression(exp);
- return visitor.wasFound;
- }
-
- /// Returns true if [exp] has no side effects and has a constant value within
- /// any given activation of the enclosing method.
- bool isEffectivelyConstant(Expression exp) {
- // TODO(asgerf): Can be made more aggressive e.g. by checking conditional
- // expressions recursively. Determine if that is a valuable optimization
- // and/or if it is better handled at the CPS level.
- return exp is Constant ||
- exp is This ||
- exp is CreateInvocationMirror ||
- exp is CreateInstance ||
- exp is CreateBox ||
- exp is TypeExpression ||
- exp is GetStatic && exp.element.isFunction ||
- exp is Interceptor ||
- exp is ApplyBuiltinOperator ||
- exp is VariableUse && constantEnvironment.containsKey(exp.variable);
- }
-
- /// True if [node] is an assignment that can be propagated as a constant.
- bool isEffectivelyConstantAssignment(Expression node) {
- return node is Assign &&
- node.variable.writeCount == 1 &&
- isEffectivelyConstant(node.value);
- }
-
- Statement visitExpressionStatement(ExpressionStatement inputNode) {
- // Analyze chains of expression statements.
- // To avoid deep recursion, [processExpressionStatement] returns a callback
- // to invoke after its successor node has been processed.
- // These callbacks are stored in a list and invoked in reverse at the end.
- List<Function> stack = [];
- Statement node = inputNode;
- while (node is ExpressionStatement) {
- stack.add(processExpressionStatement(node));
- node = node.next;
- }
- Statement result = visitStatement(node);
- for (Function fun in stack.reversed) {
- result = fun(result);
- }
- return result;
- }
-
- /// Attempts to propagate an assignment in an expression statement.
- ///
- /// Returns a callback to be invoked after the sucessor statement has
- /// been processed.
- Function processExpressionStatement(ExpressionStatement stmt) {
- Variable leftHand = getLeftHand(stmt.expression);
- pushDominatingAssignment(leftHand);
- if (isEffectivelyConstantAssignment(stmt.expression) &&
- !usesRecentlyAssignedVariable(stmt.expression)) {
- Assign assign = stmt.expression;
- // Handle constant assignments specially.
- // They are always safe to propagate (though we should avoid duplication).
- // Moreover, they should not prevent other expressions from propagating.
- if (assign.variable.readCount == 1) {
- // A single-use constant should always be propagated to its use site.
- constantEnvironment[assign.variable] = assign.value;
- return (Statement next) {
- popDominatingAssignment(leftHand);
- if (assign.variable.readCount > 0) {
- // The assignment could not be propagated into the successor,
- // either because it [hasUnsafeVariableUse] or because the
- // use is outside the current try block, and we do not currently
- // support constant propagation out of a try block.
- constantEnvironment.remove(assign.variable);
- assign.value = visitExpression(assign.value);
- stmt.next = next;
- return stmt;
- } else {
- --assign.variable.writeCount;
- return next;
- }
- };
- } else {
- // With more than one use, we cannot propagate the constant.
- // Visit the following statement without polluting [environment] so
- // that any preceding non-constant assignments might still propagate.
- return (Statement next) {
- stmt.next = next;
- popDominatingAssignment(leftHand);
- assign.value = visitExpression(assign.value);
- return stmt;
- };
- }
- } else {
- // Try to propagate the expression, and block previous impure expressions
- // until this has propagated.
- environment.add(stmt.expression);
- return (Statement next) {
- stmt.next = next;
- popDominatingAssignment(leftHand);
- if (!environment.isEmpty && environment.last == stmt.expression) {
- // Retain the expression statement.
- environment.removeLast();
- stmt.expression = visitExpression(stmt.expression);
- return stmt;
- } else {
- // Expression was propagated into the successor.
- return stmt.next;
- }
- };
- }
- }
-
- Expression visitAssign(Assign node) {
- allowRhsPropagation.add(true);
- node.value = visitExpression(node.value);
- allowRhsPropagation.removeLast();
- // Remove assignments to variables without any uses. This can happen
- // because the assignment was propagated into its use, e.g:
- //
- // { x = foo(); bar(x) } ==> bar(x = foo()) ==> bar(foo())
- //
- if (node.variable.readCount == 0) {
- --node.variable.writeCount;
- return node.value;
- }
- return node;
- }
-
- /// Process nodes right-to-left, the opposite of evaluation order in the case
- /// of argument lists..
- void _rewriteList(List<Node> nodes, {bool rhsPropagation: true}) {
- allowRhsPropagation.add(rhsPropagation);
- for (int i = nodes.length - 1; i >= 0; --i) {
- nodes[i] = visitExpression(nodes[i]);
- }
- allowRhsPropagation.removeLast();
- }
-
- Expression visitInvokeStatic(InvokeStatic node) {
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitInvokeMethod(InvokeMethod node) {
- if (node.receiverIsNotNull) {
- _rewriteList(node.arguments);
- node.receiver = visitExpression(node.receiver);
- } else {
- // Impure expressions cannot be propagated across the method lookup,
- // because it throws when the receiver is null.
- inEmptyEnvironment(() {
- _rewriteList(node.arguments);
- });
- node.receiver = visitExpression(node.receiver);
- }
- return node;
- }
-
- Expression visitOneShotInterceptor(OneShotInterceptor node) {
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitApplyBuiltinMethod(ApplyBuiltinMethod node) {
- if (node.receiverIsNotNull) {
- _rewriteList(node.arguments);
- node.receiver = visitExpression(node.receiver);
- } else {
- // Impure expressions cannot be propagated across the method lookup,
- // because it throws when the receiver is null.
- inEmptyEnvironment(() {
- _rewriteList(node.arguments);
- });
- node.receiver = visitExpression(node.receiver);
- }
- return node;
- }
-
- Expression visitInvokeMethodDirectly(InvokeMethodDirectly node) {
- _rewriteList(node.arguments);
- // The target function might not exist before the enclosing class has been
- // instantitated for the first time. If the receiver might be the first
- // instantiation of its class, we cannot propgate it into the receiver
- // expression, because the target function is evaluated before the receiver.
- // Calls to constructor bodies are compiled so that the receiver is
- // evaluated first, so they are safe.
- if (node.target is! ConstructorBodyElement) {
- inEmptyEnvironment(() {
- node.receiver = visitExpression(node.receiver);
- });
- } else {
- node.receiver = visitExpression(node.receiver);
- }
- return node;
- }
-
- Expression visitInvokeConstructor(InvokeConstructor node) {
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitConditional(Conditional node) {
- // Conditional expressions do not exist in the input, but they are
- // introduced by if-to-conditional conversion.
- // Their subexpressions have already been processed; do not reprocess them.
- //
- // Note that this can only happen for conditional expressions. It is an
- // error for any other type of expression to be visited twice or to be
- // created and then visited. We use this special treatment of conditionals
- // to allow for assignment inlining after if-to-conditional conversion.
- //
- // There are several reasons we should not reprocess the subexpressions:
- //
- // - It will mess up the [seenUses] counter, since a single use will be
- // counted twice.
- //
- // - Other visit methods assume that all subexpressions are variable uses
- // because they come fresh out of the tree IR builder.
- //
- // - Reprocessing can be expensive.
- //
- return node;
- }
-
- Expression visitLogicalOperator(LogicalOperator node) {
- // Impure expressions may not propagate across the branch.
- inEmptyEnvironment(() {
- node.right = visitExpression(node.right);
- });
- node.left = visitExpression(node.left);
- return node;
- }
-
- Expression visitNot(Not node) {
- node.operand = visitExpression(node.operand);
- return node;
- }
-
- bool isNullConstant(Expression node) {
- return node is Constant && node.value.isNull;
- }
-
- Statement visitReturn(Return node) {
- if (!isNullConstant(node.value)) {
- // Do not chain assignments into a null return.
- node.value = visitExpression(node.value);
- }
- return node;
- }
-
- Statement visitThrow(Throw node) {
- node.value = visitExpression(node.value);
- return node;
- }
-
- Statement visitUnreachable(Unreachable node) {
- return node;
- }
-
- Statement visitBreak(Break node) {
- // Redirect through chain of breaks.
- // Note that useCount was accounted for at visitLabeledStatement.
- // Note redirect may return either a Break or Continue statement.
- Jump jump = redirect(node);
- if (jump is Break &&
- jump.target.useCount == 1 &&
- safeForInlining.contains(jump.target)) {
- --jump.target.useCount;
- return visitStatement(jump.target.binding.next);
- }
- return jump;
- }
-
- Statement visitContinue(Continue node) {
- return node;
- }
-
- Statement visitLabeledStatement(LabeledStatement node) {
- if (node.next is Jump) {
- // Eliminate label if next is a break or continue statement
- // Breaks to this label are redirected to the outer label.
- // Note that breakCount for the two labels is updated proactively here
- // so breaks can reliably tell if they should inline their target.
- Jump next = node.next;
- Jump newJump = redirect(next);
- labelRedirects[node.label] = newJump;
- newJump.target.useCount += node.label.useCount - 1;
- node.label.useCount = 0;
- Statement result = visitStatement(node.body);
- labelRedirects.remove(node.label); // Save some space.
- return result;
- }
-
- safeForInlining.add(node.label);
- node.body = visitStatement(node.body);
- safeForInlining.remove(node.label);
-
- if (node.label.useCount == 0) {
- // Eliminate the label if next was inlined at a break
- return node.body;
- }
-
- // Do not propagate assignments into the successor statements, since they
- // may be overwritten by assignments in the body.
- inEmptyEnvironment(() {
- node.next = visitStatement(node.next);
- });
-
- return node;
- }
-
- Statement visitIf(If node) {
- // Do not propagate assignments into branches.
- inEmptyEnvironment(() {
- node.thenStatement = visitStatement(node.thenStatement);
- node.elseStatement = visitStatement(node.elseStatement);
- });
-
- node.condition = visitExpression(node.condition);
-
- inEmptyEnvironment(() {
- tryCollapseIf(node);
- });
-
- Statement reduced = combineStatementsInBranches(
- node.thenStatement, node.elseStatement, node.condition);
- if (reduced != null) {
- return reduced;
- }
-
- return node;
- }
-
- Statement visitWhileTrue(WhileTrue node) {
- // Do not propagate assignments into loops. Doing so is not safe for
- // variables modified in the loop (the initial value will be propagated).
- // Do not propagate effective constant expressions into loops, since
- // computing them is not free (e.g. interceptors are expensive).
- inEmptyEnvironment(() {
- node.body = visitStatement(node.body);
- }, keepConstants: false);
- return node;
- }
-
- Statement visitFor(For node) {
- // Not introduced yet
- throw "Unexpected For in StatementRewriter";
- }
-
- Statement visitTry(Try node) {
- inEmptyEnvironment(() {
- Set<Label> saved = safeForInlining;
- safeForInlining = new Set<Label>();
- node.tryBody = visitStatement(node.tryBody);
- safeForInlining = saved;
- node.catchParameters.forEach(pushDominatingAssignment);
- node.catchBody = visitStatement(node.catchBody);
- node.catchParameters.forEach(popDominatingAssignment);
- });
- return node;
- }
-
- Expression visitConstant(Constant node) {
- if (isRhsPropagationAllowed && !environment.isEmpty) {
- Constant constant = getRightHandConstant(environment.last);
- if (constant != null && constant.value == node.value) {
- return visitExpression(environment.removeLast());
- }
- }
- return node;
- }
-
- Expression visitThis(This node) {
- return node;
- }
-
- Expression visitLiteralList(LiteralList node) {
- _rewriteList(node.values);
- return node;
- }
-
- Expression visitTypeOperator(TypeOperator node) {
- _rewriteList(node.typeArguments);
- node.value = visitExpression(node.value);
- return node;
- }
-
- bool isCompoundableBuiltin(Expression e) {
- return e is ApplyBuiltinOperator &&
- e.arguments.length >= 2 &&
- isCompoundableOperator(e.operator);
- }
-
- /// Converts a compoundable operator application into the right-hand side for
- /// use in a compound assignment, discarding the left-hand value.
- ///
- /// For example, for `x + y + z` it returns `y + z`.
- Expression contractCompoundableBuiltin(ApplyBuiltinOperator e) {
- assert(isCompoundableBuiltin(e));
- if (e.arguments.length > 2) {
- assert(e.operator == BuiltinOperator.StringConcatenate);
- return new ApplyBuiltinOperator(
- e.operator, e.arguments.skip(1).toList(), e.sourceInformation);
- } else {
- return e.arguments[1];
- }
- }
-
- void destroyVariableUse(VariableUse node) {
- --node.variable.readCount;
- }
-
- Expression visitSetField(SetField node) {
- allowRhsPropagation.add(true);
- node.value = visitExpression(node.value);
- if (isCompoundableBuiltin(node.value)) {
- ApplyBuiltinOperator rhs = node.value;
- Expression left = rhs.arguments[0];
- if (left is GetField &&
- left.field == node.field &&
- samePrimary(left.object, node.object)) {
- destroyPrimaryExpression(left.object);
- node.compound = rhs.operator;
- node.value = contractCompoundableBuiltin(rhs);
- }
- }
- node.object = visitExpression(node.object);
- allowRhsPropagation.removeLast();
- return node;
- }
-
- Expression visitGetField(GetField node) {
- node.object = visitExpression(node.object);
- return node;
- }
-
- Expression visitGetStatic(GetStatic node) {
- return node;
- }
-
- Expression visitSetStatic(SetStatic node) {
- allowRhsPropagation.add(true);
- node.value = visitExpression(node.value);
- if (isCompoundableBuiltin(node.value)) {
- ApplyBuiltinOperator rhs = node.value;
- Expression left = rhs.arguments[0];
- if (left is GetStatic &&
- left.element == node.element &&
- !left.useLazyGetter) {
- node.compound = rhs.operator;
- node.value = contractCompoundableBuiltin(rhs);
- }
- }
- allowRhsPropagation.removeLast();
- return node;
- }
-
- Expression visitGetTypeTestProperty(GetTypeTestProperty node) {
- node.object = visitExpression(node.object);
- return node;
- }
-
- Expression visitCreateBox(CreateBox node) {
- return node;
- }
-
- Expression visitCreateInstance(CreateInstance node) {
- if (node.typeInformation != null) {
- node.typeInformation = visitExpression(node.typeInformation);
- }
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitReifyRuntimeType(ReifyRuntimeType node) {
- node.value = visitExpression(node.value);
- return node;
- }
-
- Expression visitReadTypeVariable(ReadTypeVariable node) {
- node.target = visitExpression(node.target);
- return node;
- }
-
- Expression visitTypeExpression(TypeExpression node) {
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitCreateInvocationMirror(CreateInvocationMirror node) {
- _rewriteList(node.arguments);
- return node;
- }
-
- Expression visitInterceptor(Interceptor node) {
- node.input = visitExpression(node.input);
- return node;
- }
-
- Expression visitGetLength(GetLength node) {
- node.object = visitExpression(node.object);
- return node;
- }
-
- Expression visitGetIndex(GetIndex node) {
- node.index = visitExpression(node.index);
- node.object = visitExpression(node.object);
- return node;
- }
-
- Expression visitSetIndex(SetIndex node) {
- node.value = visitExpression(node.value);
- if (isCompoundableBuiltin(node.value)) {
- ApplyBuiltinOperator rhs = node.value;
- Expression left = rhs.arguments[0];
- if (left is GetIndex &&
- samePrimary(left.object, node.object) &&
- samePrimary(left.index, node.index)) {
- destroyPrimaryExpression(left.object);
- destroyPrimaryExpression(left.index);
- node.compound = rhs.operator;
- node.value = contractCompoundableBuiltin(rhs);
- }
- }
- node.index = visitExpression(node.index);
- node.object = visitExpression(node.object);
- return node;
- }
-
- /// True if [operator] is a binary operator that always has the same value
- /// if its arguments are swapped.
- bool isSymmetricOperator(BuiltinOperator operator) {
- switch (operator) {
- case BuiltinOperator.StrictEq:
- case BuiltinOperator.StrictNeq:
- case BuiltinOperator.LooseEq:
- case BuiltinOperator.LooseNeq:
- case BuiltinOperator.NumAnd:
- case BuiltinOperator.NumOr:
- case BuiltinOperator.NumXor:
- case BuiltinOperator.NumAdd:
- case BuiltinOperator.NumMultiply:
- return true;
- default:
- return false;
- }
- }
-
- /// If [operator] is a commutable binary operator, returns the commuted
- /// operator, possibly the operator itself, otherwise returns `null`.
- BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) {
- if (isSymmetricOperator(operator)) {
- // Symmetric operators are their own commutes.
- return operator;
- }
- switch (operator) {
- case BuiltinOperator.NumLt:
- return BuiltinOperator.NumGt;
- case BuiltinOperator.NumLe:
- return BuiltinOperator.NumGe;
- case BuiltinOperator.NumGt:
- return BuiltinOperator.NumLt;
- case BuiltinOperator.NumGe:
- return BuiltinOperator.NumLe;
- default:
- return null;
- }
- }
-
- /// Built-in binary operators are commuted when it is safe and can enable an
- /// assignment propagation. For example:
- ///
- /// var x = foo();
- /// var y = bar();
- /// var z = y < x;
- ///
- /// ==>
- ///
- /// var z = foo() > bar();
- ///
- /// foo() must be evaluated before bar(), so the propagation is only possible
- /// by commuting the operator.
- Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
- if (!environment.isEmpty && getLeftHand(environment.last) != null) {
- Variable propagatableVariable = getLeftHand(environment.last);
- BuiltinOperator commuted = commuteBinaryOperator(node.operator);
- if (commuted != null) {
- // Only binary operators can commute.
- assert(node.arguments.length == 2);
- Expression left = node.arguments[0];
- if (left is VariableUse && propagatableVariable == left.variable) {
- Expression right = node.arguments[1];
- if (right is This ||
- (right is VariableUse &&
- propagatableVariable != right.variable &&
- !constantEnvironment.containsKey(right.variable))) {
- // An assignment can be propagated if we commute the operator.
- node.operator = commuted;
- node.arguments[0] = right;
- node.arguments[1] = left;
- }
- }
- }
- }
- // Avoid code like `p == (q.f = null)`. JS operators with a constant operand
- // can sometimes be compiled to a specialized instruction in the JS engine,
- // so retain syntactically constant operands.
- _rewriteList(node.arguments, rhsPropagation: false);
- return node;
- }
-
- /// If [s] and [t] are similar statements we extract their subexpressions
- /// and returns a new statement of the same type using expressions combined
- /// with the [combine] callback. For example:
- ///
- /// combineStatements(Return E1, Return E2) = Return combine(E1, E2)
- ///
- /// If [combine] returns E1 then the unified statement is equivalent to [s],
- /// and if [combine] returns E2 the unified statement is equivalence to [t].
- ///
- /// It is guaranteed that no side effects occur between the beginning of the
- /// statement and the position of the combined expression.
- ///
- /// Returns null if the statements are too different.
- ///
- /// If non-null is returned, the caller MUST discard [s] and [t] and use
- /// the returned statement instead.
- Statement combineStatementsInBranches(
- Statement s, Statement t, Expression condition) {
- if (s is Return && t is Return) {
- return new Return(new Conditional(condition, s.value, t.value));
- }
- if (s is ExpressionStatement && t is ExpressionStatement) {
- // Combine the two expressions and the two successor statements.
- //
- // C ? {E1 ; S1} : {E2 ; S2}
- // ==>
- // (C ? E1 : E2) : combine(S1, S2)
- //
- // If E1 and E2 are assignments, we want to propagate these into the
- // combined statement.
- //
- // It might not be possible to combine the statements, so we combine the
- // expressions, put the result in the environment, and then uncombine the
- // expressions if the statements could not be combined.
-
- // Combine the expressions.
- CombinedExpressions values =
- combineAsConditional(s.expression, t.expression, condition);
-
- // Put this into the environment and try to combine the statements.
- // We are not in risk of reprocessing the original subexpressions because
- // the combined expression will always hide them inside a Conditional.
- environment.add(values.combined);
-
- Variable leftHand = getLeftHand(values.combined);
- pushDominatingAssignment(leftHand);
- Statement next = combineStatements(s.next, t.next);
- popDominatingAssignment(leftHand);
-
- if (next == null) {
- // Statements could not be combined.
- // Restore the environment and uncombine expressions again.
- environment.removeLast();
- values.uncombine();
- return null;
- } else if (!environment.isEmpty && environment.last == values.combined) {
- // Statements were combined but the combined expression could not be
- // propagated. Leave it as an expression statement here.
- environment.removeLast();
- s.expression = values.combined;
- s.next = next;
- return s;
- } else {
- // Statements were combined and the combined expressions were
- // propagated into the combined statement.
- return next;
- }
- }
- return null;
- }
-
- /// Creates the expression `[condition] ? [s] : [t]` or an equivalent
- /// expression if something better can be done.
- ///
- /// In particular, assignments will be merged as follows:
- ///
- /// C ? (v = E1) : (v = E2)
- /// ==>
- /// v = C ? E1 : E2
- ///
- /// The latter form is more compact and can also be inlined.
- CombinedExpressions combineAsConditional(
- Expression s, Expression t, Expression condition) {
- if (s is Assign && t is Assign && s.variable == t.variable) {
- Expression values = new Conditional(condition, s.value, t.value);
- return new CombinedAssigns(s, t, new CombinedExpressions(values));
- }
- return new CombinedExpressions(new Conditional(condition, s, t));
- }
-
- /// Returns a statement equivalent to both [s] and [t], or null if [s] and
- /// [t] are incompatible.
- /// If non-null is returned, the caller MUST discard [s] and [t] and use
- /// the returned statement instead.
- /// If two breaks are combined, the label's break counter will be decremented.
- Statement combineStatements(Statement s, Statement t) {
- if (s is Break && t is Break && s.target == t.target) {
- --t.target.useCount; // Two breaks become one.
- if (s.target.useCount == 1 && safeForInlining.contains(s.target)) {
- // Only one break remains; inline it.
- --s.target.useCount;
- return visitStatement(s.target.binding.next);
- }
- return s;
- }
- if (s is Continue && t is Continue && s.target == t.target) {
- --t.target.useCount; // Two continues become one.
- return s;
- }
- if (s is Return && t is Return) {
- CombinedExpressions values = combineExpressions(s.value, t.value);
- if (values != null) {
- // TODO(johnniwinther): Handle multiple source informations.
- SourceInformation sourceInformation = s.sourceInformation != null
- ? s.sourceInformation
- : t.sourceInformation;
- return new Return(values.combined,
- sourceInformation: sourceInformation);
- }
- }
- if (s is ExpressionStatement && t is ExpressionStatement) {
- CombinedExpressions values =
- combineExpressions(s.expression, t.expression);
- if (values == null) return null;
- environment.add(values.combined);
- Variable leftHand = getLeftHand(values.combined);
- pushDominatingAssignment(leftHand);
- Statement next = combineStatements(s.next, t.next);
- popDominatingAssignment(leftHand);
- if (next == null) {
- // The successors could not be combined.
- // Restore the environment and uncombine the values again.
- assert(environment.last == values.combined);
- environment.removeLast();
- values.uncombine();
- return null;
- } else if (!environment.isEmpty && environment.last == values.combined) {
- // The successors were combined but the combined expressions were not
- // propagated. Leave the combined expression as a statement.
- environment.removeLast();
- s.expression = values.combined;
- s.next = next;
- return s;
- } else {
- // The successors were combined, and the combined expressions were
- // propagated into the successors.
- return next;
- }
- }
- return null;
- }
-
- /// Returns an expression equivalent to both [e1] and [e2].
- /// If non-null is returned, the caller must discard [e1] and [e2] and use
- /// the resulting expression in the tree.
- CombinedExpressions combineExpressions(Expression e1, Expression e2) {
- if (e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable) {
- return new CombinedUses(e1, e2);
- }
- if (e1 is Assign && e2 is Assign && e1.variable == e2.variable) {
- CombinedExpressions values = combineExpressions(e1.value, e2.value);
- if (values != null) {
- return new CombinedAssigns(e1, e2, values);
- }
- }
- if (e1 is Constant && e2 is Constant && e1.value == e2.value) {
- return new CombinedExpressions(e1);
- }
- return null;
- }
-
- /// Try to collapse nested ifs using && and || expressions.
- /// For example:
- ///
- /// if (E1) { if (E2) S else break L } else break L
- /// ==>
- /// if (E1 && E2) S else break L
- ///
- /// [branch1] and [branch2] control the position of the S statement.
- ///
- /// Must be called with an empty environment.
- void tryCollapseIf(If node) {
- assert(environment.isEmpty);
- // Repeatedly try to collapse nested ifs.
- // The transformation is shrinking (destroys an if) so it remains linear.
- // Here is an example where more than one iteration is required:
- //
- // if (E1)
- // if (E2) break L2 else break L1
- // else
- // break L1
- //
- // L1.target ::=
- // if (E3) S else break L2
- //
- // After first collapse:
- //
- // if (E1 && E2)
- // break L2
- // else
- // {if (E3) S else break L2} (inlined from break L1)
- //
- // We can then do another collapse using the inlined nested if.
- bool changed = true;
- while (changed) {
- changed = false;
- if (tryCollapseIfAux(node, true, true)) {
- changed = true;
- }
- if (tryCollapseIfAux(node, true, false)) {
- changed = true;
- }
- if (tryCollapseIfAux(node, false, true)) {
- changed = true;
- }
- if (tryCollapseIfAux(node, false, false)) {
- changed = true;
- }
- }
- }
-
- bool tryCollapseIfAux(If outerIf, bool branch1, bool branch2) {
- // NOTE: We name variables here as if S is in the then-then position.
- Statement outerThen = getBranch(outerIf, branch1);
- Statement outerElse = getBranch(outerIf, !branch1);
- if (outerThen is If) {
- If innerIf = outerThen;
- Statement innerThen = getBranch(innerIf, branch2);
- Statement innerElse = getBranch(innerIf, !branch2);
- Statement combinedElse = combineStatements(innerElse, outerElse);
- if (combinedElse != null) {
- // We always put S in the then branch of the result, and adjust the
- // condition expression if S was actually found in the else branch(es).
- outerIf.condition = new LogicalOperator.and(
- makeCondition(outerIf.condition, branch1),
- makeCondition(innerIf.condition, branch2));
- outerIf.thenStatement = innerThen;
- outerIf.elseStatement = combinedElse;
- return outerIf.elseStatement is If;
- }
- }
- return false;
- }
-
- Expression makeCondition(Expression e, bool polarity) {
- return polarity ? e : new Not(e);
- }
-
- Statement getBranch(If node, bool polarity) {
- return polarity ? node.thenStatement : node.elseStatement;
- }
-
- void handleForeignCode(ForeignCode node) {
- // Some arguments will get inserted in a JS code template. The arguments
- // will not always be evaluated (e.g. the second placeholder in the template
- // '# && #').
- bool isNullable(int position) => node.nullableArguments[position];
-
- int safeArguments =
- PlaceholderSafetyAnalysis.analyze(node.codeTemplate.ast, isNullable);
- inEmptyEnvironment(() {
- for (int i = node.arguments.length - 1; i >= safeArguments; --i) {
- node.arguments[i] = visitExpression(node.arguments[i]);
- }
- });
- for (int i = safeArguments - 1; i >= 0; --i) {
- node.arguments[i] = visitExpression(node.arguments[i]);
- }
- }
-
- @override
- Expression visitForeignExpression(ForeignExpression node) {
- handleForeignCode(node);
- return node;
- }
-
- @override
- Statement visitForeignStatement(ForeignStatement node) {
- handleForeignCode(node);
- return node;
- }
-
- @override
- Expression visitAwait(Await node) {
- node.input = visitExpression(node.input);
- return node;
- }
-
- @override
- Statement visitYield(Yield node) {
- node.next = visitStatement(node.next);
- node.input = visitExpression(node.input);
- return node;
- }
-
- @override
- Statement visitReceiverCheck(ReceiverCheck node) {
- inEmptyEnvironment(() {
- node.next = visitStatement(node.next);
- });
- if (node.condition != null) {
- inEmptyEnvironment(() {
- // Value occurs in conditional context.
- node.value = visitExpression(node.value);
- });
- node.condition = visitExpression(node.condition);
- } else {
- node.value = visitExpression(node.value);
- }
- return node;
- }
-}
-
-/// Result of combining two expressions, with the potential for reverting the
-/// combination.
-///
-/// Reverting a combination is done by calling [uncombine]. In this case,
-/// both the original expressions should remain in the tree, and the [combined]
-/// expression should be orphaned.
-///
-/// Explicitly reverting a combination is necessary to maintain variable
-/// reference counts.
-abstract class CombinedExpressions {
- Expression get combined;
- void uncombine();
-
- factory CombinedExpressions(Expression e) = GenericCombinedExpressions;
-}
-
-/// Combines assignments of form `[variable] := E1` and `[variable] := E2` into
-/// a single assignment of form `[variable] := combine(E1, E2)`.
-class CombinedAssigns implements CombinedExpressions {
- Assign assign1, assign2;
- CombinedExpressions value;
- Expression combined;
-
- CombinedAssigns(this.assign1, this.assign2, this.value) {
- assert(assign1.variable == assign2.variable);
- assign1.variable.writeCount -= 2; // Destroy the two original assignemnts.
- combined = new Assign(assign1.variable, value.combined);
- }
-
- void uncombine() {
- value.uncombine();
- ++assign1.variable.writeCount; // Restore original reference count.
- }
-}
-
-/// Combines two variable uses into one.
-class CombinedUses implements CombinedExpressions {
- VariableUse use1, use2;
- Expression combined;
-
- CombinedUses(this.use1, this.use2) {
- assert(use1.variable == use2.variable);
- use1.variable.readCount -= 2; // Destroy both the original uses.
- combined = new VariableUse(use1.variable);
- }
-
- void uncombine() {
- ++use1.variable.readCount; // Restore original reference count.
- }
-}
-
-/// Result of combining two expressions that do not affect reference counting.
-class GenericCombinedExpressions implements CombinedExpressions {
- Expression combined;
-
- GenericCombinedExpressions(this.combined);
-
- void uncombine() {}
-}
-
-/// Looks for uses of a specific variable.
-///
-/// Note that this visitor is only applied to expressions where all
-/// sub-expressions are known to be variable uses, so there is no risk of
-/// explosive reprocessing.
-class IsVariableUsedVisitor extends RecursiveVisitor {
- Variable variable;
- bool wasFound = false;
-
- IsVariableUsedVisitor(this.variable);
-
- visitVariableUse(VariableUse node) {
- if (node.variable == variable) {
- wasFound = true;
- }
- }
-}
-
-typedef VariableUseCallback(VariableUse use);
-
-class VariableUseVisitor extends RecursiveVisitor {
- VariableUseCallback callback;
-
- VariableUseVisitor(this.callback);
-
- visitVariableUse(VariableUse use) => callback(use);
-
- static void visit(Expression node, VariableUseCallback callback) {
- new VariableUseVisitor(callback).visitExpression(node);
- }
-}
-
-bool sameVariable(Expression e1, Expression e2) {
- return e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable;
-}
-
-/// True if [e1] and [e2] are primary expressions (expressions without
-/// subexpressions) with the same value.
-bool samePrimary(Expression e1, Expression e2) {
- return sameVariable(e1, e2) || (e1 is This && e2 is This);
-}
-
-/// Decrement the reference count for [e] if it is a variable use.
-void destroyPrimaryExpression(Expression e) {
- if (e is VariableUse) {
- --e.variable.readCount;
- } else {
- assert(e is This);
- }
-}

Powered by Google App Engine
This is Rietveld 408576698