| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 library dart2js.cps_ir.use_field_initializers; |
| 5 |
| 6 import 'cps_ir_nodes.dart'; |
| 7 import 'optimizers.dart'; |
| 8 import '../elements/elements.dart'; |
| 9 import '../js_backend/js_backend.dart'; |
| 10 |
| 11 /// Eliminates [SetField] instructions when the value can instead be passed into |
| 12 /// the field initializer of a [CreateInstance] instruction. |
| 13 /// |
| 14 /// This compensates for a somewhat common pattern where fields are initialized |
| 15 /// in the constructor body instead of using intializers. For example: |
| 16 /// |
| 17 /// class Foo { |
| 18 /// var x, y; |
| 19 /// Foo(x, y) { |
| 20 /// this.x = x; |
| 21 /// this.y = y; |
| 22 /// } |
| 23 /// } |
| 24 /// |
| 25 /// ==> (IR for Foo constructor) |
| 26 /// |
| 27 /// foo = new D.Foo(null, null); |
| 28 /// foo.x = 'a'; |
| 29 /// foo.y = 'b'; |
| 30 /// |
| 31 /// ==> (after this pass) |
| 32 /// |
| 33 /// foo = new D.Foo('a', 'b'); |
| 34 // |
| 35 // TODO(asgerf): Store forwarding and load elimination could most likely |
| 36 // handle this more generally. |
| 37 // |
| 38 class UseFieldInitializers extends BlockVisitor implements Pass { |
| 39 String get passName => 'Use field initializers'; |
| 40 |
| 41 final JavaScriptBackend backend; |
| 42 |
| 43 final Set<CreateInstance> unescaped = new Set<CreateInstance>(); |
| 44 |
| 45 /// Continuation bindings separating the current traversal position from an |
| 46 /// unescaped [CreateInstance]. When [CreateInstance] is sunk, these |
| 47 /// continuations must sink as well to ensure the object remains in scope |
| 48 /// inside the bound continuations. |
| 49 final List<LetCont> letConts = <LetCont>[]; |
| 50 |
| 51 /// If non-null, the bindings in [letConts] should sink to immediately below |
| 52 /// this node. |
| 53 InteriorNode letContSinkTarget = null; |
| 54 EscapeVisitor escapeVisitor; |
| 55 |
| 56 UseFieldInitializers(this.backend); |
| 57 |
| 58 void rewrite(FunctionDefinition node) { |
| 59 escapeVisitor = new EscapeVisitor(this); |
| 60 BlockVisitor.traverseInPreOrder(node, this); |
| 61 } |
| 62 |
| 63 void escape(Reference ref) { |
| 64 Definition def = ref.definition; |
| 65 if (def is CreateInstance) { |
| 66 unescaped.remove(def); |
| 67 if (unescaped.isEmpty) { |
| 68 sinkLetConts(); |
| 69 letConts.clear(); |
| 70 } |
| 71 } |
| 72 } |
| 73 |
| 74 void visitContinuation(Continuation node) { |
| 75 endBasicBlock(); |
| 76 } |
| 77 void visitLetHandler(LetHandler node) { |
| 78 endBasicBlock(); |
| 79 } |
| 80 void visitInvokeContinuation(InvokeContinuation node) { |
| 81 endBasicBlock(); |
| 82 } |
| 83 void visitBranch(Branch node) { |
| 84 endBasicBlock(); |
| 85 } |
| 86 void visitRethrow(Rethrow node) { |
| 87 endBasicBlock(); |
| 88 } |
| 89 void visitThrow(Throw node) { |
| 90 endBasicBlock(); |
| 91 } |
| 92 void visitUnreachable(Unreachable node) { |
| 93 endBasicBlock(); |
| 94 } |
| 95 |
| 96 void visitLetMutable(LetMutable node) { |
| 97 escape(node.value); |
| 98 } |
| 99 |
| 100 void visitLetCont(LetCont node) { |
| 101 if (unescaped.isNotEmpty) { |
| 102 // Ensure we do not lift a LetCont if there is a sink target set above |
| 103 // the current node. |
| 104 sinkLetConts(); |
| 105 letConts.add(node); |
| 106 } |
| 107 } |
| 108 |
| 109 void sinkLetConts() { |
| 110 if (letContSinkTarget != null) { |
| 111 for (LetCont letCont in letConts.reversed) { |
| 112 letCont..remove()..insertBelow(letContSinkTarget); |
| 113 } |
| 114 letContSinkTarget = null; |
| 115 } |
| 116 } |
| 117 |
| 118 void endBasicBlock() { |
| 119 sinkLetConts(); |
| 120 letConts.clear(); |
| 121 unescaped.clear(); |
| 122 } |
| 123 |
| 124 void visitLetPrim(LetPrim node) { |
| 125 Primitive prim = node.primitive; |
| 126 if (prim is CreateInstance) { |
| 127 unescaped.add(prim); |
| 128 prim.arguments.forEach(escape); |
| 129 return; |
| 130 } |
| 131 if (unescaped.isEmpty) return; |
| 132 if (prim is SetField) { |
| 133 escape(prim.value); |
| 134 Primitive object = prim.object.definition; |
| 135 if (object is CreateInstance && unescaped.contains(object)) { |
| 136 int index = getFieldIndex(object.classElement, prim.field); |
| 137 if (index == -1) { |
| 138 // This field is not initialized at creation time, so we cannot pull |
| 139 // set SetField into the CreateInstance instruction. We have to |
| 140 // leave the instruction here, and this counts as a use of the object. |
| 141 escape(prim.object); |
| 142 } else { |
| 143 // Replace the field initializer with the new value. There are no uses |
| 144 // of the object before this, so the old value cannot have been seen. |
| 145 object.arguments[index].changeTo(prim.value.definition); |
| 146 prim.destroy(); |
| 147 // The right-hand side might not be in scope at the CreateInstance. |
| 148 // Sink the creation down to this point. |
| 149 rebindCreateInstanceAt(object, node); |
| 150 letContSinkTarget = node; |
| 151 } |
| 152 } |
| 153 return; |
| 154 } |
| 155 if (prim is GetField) { |
| 156 // When reading the field of a newly created object, just use the initial |
| 157 // value and destroy the GetField. This can unblock the other optimization |
| 158 // since we remove a use of the object. |
| 159 Primitive object = prim.object.definition; |
| 160 if (object is CreateInstance && unescaped.contains(object)) { |
| 161 int index = getFieldIndex(object.classElement, prim.field); |
| 162 if (index == -1) { |
| 163 escape(prim.object); |
| 164 } else { |
| 165 prim.replaceUsesWith(object.arguments[index].definition); |
| 166 prim.destroy(); |
| 167 node.remove(); |
| 168 } |
| 169 } |
| 170 return; |
| 171 } |
| 172 escapeVisitor.visit(node.primitive); |
| 173 } |
| 174 |
| 175 void rebindCreateInstanceAt(CreateInstance prim, LetPrim newBinding) { |
| 176 removeBinding(prim); |
| 177 newBinding.primitive = prim; |
| 178 prim.parent = newBinding; |
| 179 } |
| 180 |
| 181 /// Returns the index of [field] in the canonical initialization order in |
| 182 /// [classElement], or -1 if the field is not initialized at creation time |
| 183 /// for that class. |
| 184 int getFieldIndex(ClassElement classElement, FieldElement field) { |
| 185 // There is no stored map from a field to its index in a given class, so we |
| 186 // have to iterate over all instance fields until we find it. |
| 187 int current = -1, index = -1; |
| 188 classElement.forEachInstanceField((host, currentField) { |
| 189 if (!backend.isNativeOrExtendsNative(host)) { |
| 190 ++current; |
| 191 if (currentField == field) { |
| 192 index = current; |
| 193 } |
| 194 } |
| 195 }, includeSuperAndInjectedMembers: true); |
| 196 return index; |
| 197 } |
| 198 |
| 199 void removeBinding(Primitive prim) { |
| 200 LetPrim node = prim.parent; |
| 201 node.remove(); |
| 202 } |
| 203 } |
| 204 |
| 205 class EscapeVisitor extends DeepRecursiveVisitor { |
| 206 final UseFieldInitializers main; |
| 207 EscapeVisitor(this.main); |
| 208 |
| 209 processReference(Reference ref) { |
| 210 main.escape(ref); |
| 211 } |
| 212 } |
| OLD | NEW |