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

Unified Diff: pkg/compiler/lib/src/cps_ir/mutable_ssa.dart

Issue 1234753003: dart2js cps: Rewrite mutable variables to continuation parameters. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Add tests and fix an assertion Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart ('k') | pkg/compiler/lib/src/cps_ir/optimizers.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/cps_ir/mutable_ssa.dart
diff --git a/pkg/compiler/lib/src/cps_ir/mutable_ssa.dart b/pkg/compiler/lib/src/cps_ir/mutable_ssa.dart
new file mode 100644
index 0000000000000000000000000000000000000000..abf85cd611c1bec8c1aaf9436bd15871af01e08a
--- /dev/null
+++ b/pkg/compiler/lib/src/cps_ir/mutable_ssa.dart
@@ -0,0 +1,252 @@
+// Copyright (c) 2015, 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 dart2js.cps_ir.mutable_ssa;
+
+import 'cps_ir_nodes.dart';
+import 'optimizers.dart';
+
+/// Determines which mutable variables should be rewritten to phi assignments
+/// in this pass.
+///
+/// We do not rewrite variables that have an assignment inside a try block that
+/// does not contain its declaration.
+class MutableVariablePreanalysis extends RecursiveVisitor {
+ // Number of try blocks enclosing the current position.
+ int currentDepth = 0;
+
+ /// Number of try blocks enclosing the declaration of a given mutable
+ /// variable.
+ ///
+ /// All mutable variables seen will be present in the map after the analysis.
+ Map<MutableVariable, int> variableDepth = <MutableVariable, int>{};
+
+ /// Variables with an assignment inside a try block that does not contain
+ /// its declaration.
+ Set<MutableVariable> hasAssignmentInTry = new Set<MutableVariable>();
+
+ void visitLetHandler(LetHandler node) {
+ ++currentDepth;
+ visit(node.body);
+ --currentDepth;
+ visit(node.handler);
+ }
+
+ void processLetMutable(LetMutable node) {
+ variableDepth[node.variable] = currentDepth;
+ }
+
+ void processSetMutableVariable(SetMutableVariable node) {
+ MutableVariable variable = node.variable.definition;
+ if (currentDepth > variableDepth[variable]) {
+ hasAssignmentInTry.add(variable);
+ }
+ }
+
+ /// True if there are no mutable variables or they are all assigned inside
+ /// a try block. In this case, there is nothing to do and the pass should
+ /// be skipped.
+ bool get allMutablesAreAssignedInTryBlocks {
+ return hasAssignmentInTry.length == variableDepth.length;
+ }
+}
+
+/// Replaces mutable variables with continuation parameters, effectively
+/// bringing them into SSA form.
+///
+/// This pass is intended to clean up mutable variables that were introduced
+/// by an optimization in the type propagation pass.
+///
+/// This implementation potentially creates a lot of redundant and dead phi
+/// parameters. These will be cleaned up by redundant phi elimination and
+/// shrinking reductions.
+///
+/// Discussion:
+/// For methods with a lot of mutable variables, creating all the spurious
+/// parameters might be too expensive. If this is the case, we should
+/// improve this pass to avoid most spurious parameters in practice.
+class MutableVariableEliminator implements Pass {
+ String get passName => 'Mutable variable elimination';
+
+ /// Mutable variables currently in scope, in order of declaration.
+ /// This list determines the order of the corresponding phi parameters.
+ final List<MutableVariable> mutableVariables = <MutableVariable>[];
+
+ /// Number of phi parameters added to the given continuation.
+ final Map<Continuation, int> continuationPhiCount = <Continuation, int>{};
+
+ /// Stack of yet unprocessed continuations interleaved with the
+ /// mutable variables currently in scope.
+ ///
+ /// Continuations are processed when taken off the stack and mutable
+ /// variables fall out of scope (i.e. removed from [mutableVariables]) when
+ /// taken off the stack.
+ final List<StackItem> stack = <StackItem>[];
+
+ MutableVariablePreanalysis analysis;
+
+ void rewrite(FunctionDefinition node) {
+ analysis = new MutableVariablePreanalysis()..visit(node);
+ if (analysis.allMutablesAreAssignedInTryBlocks) {
+ // Skip the pass if there is nothing to do.
+ return;
+ }
+ processBlock(node.body, <MutableVariable, Primitive>{});
+ while (stack.isNotEmpty) {
+ StackItem item = stack.removeLast();
+ if (item is ContinuationItem) {
+ processBlock(item.continuation.body, item.environment);
+ } else {
+ assert(item is VariableItem);
+ mutableVariables.removeLast();
+ }
+ }
+ }
+
+ bool shouldRewrite(MutableVariable variable) {
+ return !analysis.hasAssignmentInTry.contains(variable);
+ }
+
+ bool isJoinContinuation(Continuation cont) {
+ return !cont.hasExactlyOneUse ||
+ cont.firstRef.parent is InvokeContinuation;
+ }
+
+ void removeNode(InteriorNode node) {
+ InteriorNode parent = node.parent;
+ parent.body = node.body;
+ node.body.parent = parent;
+ }
+
+ /// If some useful source information is attached to exactly one of the
+ /// two definitions, the information is copied onto the other.
+ void mergeHints(MutableVariable variable, Primitive value) {
+ if (variable.hint == null) {
+ variable.hint = value.hint;
+ } else if (value.hint == null) {
+ value.hint = variable.hint;
+ }
+ }
+
+ /// Processes a basic block, replacing mutable variable uses with direct
+ /// references to their values.
+ ///
+ /// [environment] is the current value of each mutable variable. The map
+ /// will be mutated during the processing.
+ ///
+ /// Continuations to be processed are put on the stack for later processing.
+ void processBlock(Expression node,
+ Map<MutableVariable, Primitive> environment) {
+ for (; node is! TailExpression; node = node.next) {
+ if (node is LetMutable && shouldRewrite(node.variable)) {
+ // Put the new mutable variable on the stack while processing the body,
+ // and pop it off again when done with the body.
+ mutableVariables.add(node.variable);
+ stack.add(new VariableItem());
+
+ // Put the initial value into the environment.
+ Primitive value = node.value.definition;
+ environment[node.variable] = value;
+
+ // Preserve variable names.
+ mergeHints(node.variable, value);
+
+ // Remove the mutable variable binding.
+ node.value.unlink();
+ removeNode(node);
+ } else if (node is SetMutableVariable &&
+ shouldRewrite(node.variable.definition)) {
+ // As above, update the environment, preserve variables and remove
+ // the mutable variable assignment.
+ MutableVariable variable = node.variable.definition;
+ environment[variable] = node.value.definition;
+ mergeHints(variable, node.value.definition);
+ node.value.unlink();
+ removeNode(node);
+ } else if (node is LetPrim && node.primitive is GetMutableVariable) {
+ GetMutableVariable getter = node.primitive;
+ MutableVariable variable = getter.variable.definition;
+ if (shouldRewrite(variable)) {
+ // Replace with the reaching definition from the environment.
+ Primitive value = environment[variable];
+ value.substituteFor(getter);
+ mergeHints(variable, value);
+ removeNode(node);
+ }
+ } else if (node is LetCont) {
+ // Create phi parameters for each join continuation bound here, and put
+ // them on the stack for later processing.
+ // Note that non-join continuations are handled at the use-site.
+ for (Continuation cont in node.continuations) {
+ if (!isJoinContinuation(cont)) continue;
+ // Create a phi parameter for every mutable variable in scope.
+ // At the same time, build the environment to use for processing
+ // the continuation (mapping mutables to phi parameters).
+ continuationPhiCount[cont] = mutableVariables.length;
+ Map<MutableVariable, Primitive> environment =
+ <MutableVariable, Primitive>{};
+ for (MutableVariable variable in mutableVariables) {
+ Parameter phi = new Parameter(variable.hint);
+ cont.parameters.add(phi);
+ phi.parent = cont;
+ environment[variable] = phi;
+ }
+ stack.add(new ContinuationItem(cont, environment));
+ }
+ } else if (node is LetHandler) {
+ // Process the catch block later and continue into the try block.
+ // We can use the same environment object for the try and catch blocks.
+ // The current environment bindings cannot change inside the try block
+ // because we exclude all variables assigned inside a try block.
+ // The environment might be extended with more bindings before we
+ // analyze the catch block, but that's ok.
+ stack.add(new ContinuationItem(node.handler, environment));
+ }
+ }
+
+ // Analyze the terminal node.
+ if (node is InvokeContinuation) {
+ Continuation cont = node.continuation.definition;
+ if (cont.isReturnContinuation) return;
+ // This is a call to a join continuation. Add arguments for the phi
+ // parameters that were added to this continuation.
+ int phiCount = continuationPhiCount[cont];
+ for (int i = 0; i < phiCount; ++i) {
+ Primitive value = environment[mutableVariables[i]];
+ Reference<Primitive> arg = new Reference<Primitive>(value);
+ node.arguments.add(arg);
+ arg.parent = node;
+ }
+ } else if (node is Branch) {
+ // Enqueue both branches with the current environment.
+ // Clone the environments once so the processing of one branch does not
+ // mutate the environment needed to process the other branch.
+ stack.add(new ContinuationItem(
+ node.trueContinuation.definition,
+ new Map<MutableVariable, Primitive>.from(environment)));
+ stack.add(new ContinuationItem(
+ node.falseContinuation.definition,
+ environment));
+ } else {
+ assert(node is Throw || node is Unreachable);
+ }
+ }
+}
+
+abstract class StackItem {}
+
+/// Represents a mutable variable that is in scope.
+///
+/// The topmost mutable variable falls out of scope when this item is
+/// taken off the stack.
+class VariableItem extends StackItem {}
+
+/// Represents a yet unprocessed continuation together with the
+/// environment in which to process it.
+class ContinuationItem extends StackItem {
+ final Continuation continuation;
+ final Map<MutableVariable, Primitive> environment;
+
+ ContinuationItem(this.continuation, this.environment);
+}
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart ('k') | pkg/compiler/lib/src/cps_ir/optimizers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698