| Index: pkg/compiler/lib/src/cps_ir/use_field_initializers.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/use_field_initializers.dart b/pkg/compiler/lib/src/cps_ir/use_field_initializers.dart
|
| deleted file mode 100644
|
| index 601974277272c99940949c0800ddb052cdde4edf..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/cps_ir/use_field_initializers.dart
|
| +++ /dev/null
|
| @@ -1,220 +0,0 @@
|
| -// Copyright (c) 2016, 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.use_field_initializers;
|
| -
|
| -import '../elements/elements.dart';
|
| -import '../js_backend/js_backend.dart';
|
| -import 'cps_ir_nodes.dart';
|
| -import 'optimizers.dart';
|
| -
|
| -/// Eliminates [SetField] instructions when the value can instead be passed into
|
| -/// the field initializer of a [CreateInstance] instruction.
|
| -///
|
| -/// This compensates for a somewhat common pattern where fields are initialized
|
| -/// in the constructor body instead of using initializers. For example:
|
| -///
|
| -/// class Foo {
|
| -/// var x, y;
|
| -/// Foo(x, y) {
|
| -/// this.x = x;
|
| -/// this.y = y;
|
| -/// }
|
| -/// }
|
| -///
|
| -/// ==> (IR for Foo constructor)
|
| -///
|
| -/// foo = new D.Foo(null, null);
|
| -/// foo.x = 'a';
|
| -/// foo.y = 'b';
|
| -///
|
| -/// ==> (after this pass)
|
| -///
|
| -/// foo = new D.Foo('a', 'b');
|
| -//
|
| -// TODO(asgerf): Store forwarding and load elimination could most likely
|
| -// handle this more generally.
|
| -//
|
| -class UseFieldInitializers extends BlockVisitor implements Pass {
|
| - String get passName => 'Use field initializers';
|
| -
|
| - final JavaScriptBackend backend;
|
| -
|
| - final Set<CreateInstance> unescaped = new Set<CreateInstance>();
|
| -
|
| - /// Continuation bindings separating the current traversal position from an
|
| - /// unescaped [CreateInstance]. When [CreateInstance] is sunk, these
|
| - /// continuations must sink as well to ensure the object remains in scope
|
| - /// inside the bound continuations.
|
| - final List<LetCont> letConts = <LetCont>[];
|
| -
|
| - /// If non-null, the bindings in [letConts] should sink to immediately below
|
| - /// this node.
|
| - InteriorNode letContSinkTarget = null;
|
| - EscapeVisitor escapeVisitor;
|
| -
|
| - UseFieldInitializers(this.backend);
|
| -
|
| - void rewrite(FunctionDefinition node) {
|
| - escapeVisitor = new EscapeVisitor(this);
|
| - BlockVisitor.traverseInPreOrder(node, this);
|
| - }
|
| -
|
| - void escape(Reference ref) {
|
| - Definition def = ref.definition;
|
| - if (def is CreateInstance) {
|
| - unescaped.remove(def);
|
| - if (unescaped.isEmpty) {
|
| - sinkLetConts();
|
| - letConts.clear();
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitContinuation(Continuation node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitLetHandler(LetHandler node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitInvokeContinuation(InvokeContinuation node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitBranch(Branch node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitRethrow(Rethrow node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitThrow(Throw node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitUnreachable(Unreachable node) {
|
| - endBasicBlock();
|
| - }
|
| -
|
| - void visitLetMutable(LetMutable node) {
|
| - escape(node.valueRef);
|
| - }
|
| -
|
| - void visitLetCont(LetCont node) {
|
| - if (unescaped.isNotEmpty) {
|
| - // Ensure we do not lift a LetCont if there is a sink target set above
|
| - // the current node.
|
| - sinkLetConts();
|
| - letConts.add(node);
|
| - }
|
| - }
|
| -
|
| - void sinkLetConts() {
|
| - if (letContSinkTarget != null) {
|
| - for (LetCont letCont in letConts.reversed) {
|
| - letCont
|
| - ..remove()
|
| - ..insertBelow(letContSinkTarget);
|
| - }
|
| - letContSinkTarget = null;
|
| - }
|
| - }
|
| -
|
| - void endBasicBlock() {
|
| - sinkLetConts();
|
| - letConts.clear();
|
| - unescaped.clear();
|
| - }
|
| -
|
| - void visitLetPrim(LetPrim node) {
|
| - Primitive prim = node.primitive;
|
| - if (prim is CreateInstance) {
|
| - unescaped.add(prim);
|
| - prim.argumentRefs.forEach(escape);
|
| - return;
|
| - }
|
| - if (unescaped.isEmpty) return;
|
| - if (prim is SetField) {
|
| - escape(prim.valueRef);
|
| - Primitive object = prim.object;
|
| - if (object is CreateInstance && unescaped.contains(object)) {
|
| - int index = getFieldIndex(object.classElement, prim.field);
|
| - if (index == -1) {
|
| - // This field is not initialized at creation time, so we cannot pull
|
| - // set SetField into the CreateInstance instruction. We have to
|
| - // leave the instruction here, and this counts as a use of the object.
|
| - escape(prim.objectRef);
|
| - } else {
|
| - // Replace the field initializer with the new value. There are no uses
|
| - // of the object before this, so the old value cannot have been seen.
|
| - object.argumentRefs[index].changeTo(prim.value);
|
| - prim.destroy();
|
| - // The right-hand side might not be in scope at the CreateInstance.
|
| - // Sink the creation down to this point.
|
| - rebindCreateInstanceAt(object, node);
|
| - letContSinkTarget = node;
|
| - }
|
| - }
|
| - return;
|
| - }
|
| - if (prim is GetField) {
|
| - // When reading the field of a newly created object, just use the initial
|
| - // value and destroy the GetField. This can unblock the other optimization
|
| - // since we remove a use of the object.
|
| - Primitive object = prim.object;
|
| - if (object is CreateInstance && unescaped.contains(object)) {
|
| - int index = getFieldIndex(object.classElement, prim.field);
|
| - if (index == -1) {
|
| - escape(prim.objectRef);
|
| - } else {
|
| - prim.replaceUsesWith(object.argument(index));
|
| - prim.destroy();
|
| - node.remove();
|
| - }
|
| - }
|
| - return;
|
| - }
|
| - escapeVisitor.visit(node.primitive);
|
| - }
|
| -
|
| - void rebindCreateInstanceAt(CreateInstance prim, LetPrim newBinding) {
|
| - removeBinding(prim);
|
| - newBinding.primitive = prim;
|
| - prim.parent = newBinding;
|
| - }
|
| -
|
| - /// Returns the index of [field] in the canonical initialization order in
|
| - /// [classElement], or -1 if the field is not initialized at creation time
|
| - /// for that class.
|
| - int getFieldIndex(ClassElement classElement, FieldElement field) {
|
| - // There is no stored map from a field to its index in a given class, so we
|
| - // have to iterate over all instance fields until we find it.
|
| - int current = -1, index = -1;
|
| - classElement.forEachInstanceField((host, currentField) {
|
| - if (!backend.isNativeOrExtendsNative(host)) {
|
| - ++current;
|
| - if (currentField == field) {
|
| - index = current;
|
| - }
|
| - }
|
| - }, includeSuperAndInjectedMembers: true);
|
| - return index;
|
| - }
|
| -
|
| - void removeBinding(Primitive prim) {
|
| - LetPrim node = prim.parent;
|
| - node.remove();
|
| - }
|
| -}
|
| -
|
| -class EscapeVisitor extends DeepRecursiveVisitor {
|
| - final UseFieldInitializers main;
|
| - EscapeVisitor(this.main);
|
| -
|
| - processReference(Reference ref) {
|
| - main.escape(ref);
|
| - }
|
| -}
|
|
|