| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, 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.scalar_replacement; | |
| 5 | |
| 6 import 'dart:collection' show Queue; | |
| 7 | |
| 8 import '../common.dart'; | |
| 9 import '../compiler.dart' as dart2js show Compiler; | |
| 10 import '../constants/values.dart'; | |
| 11 import '../elements/elements.dart'; | |
| 12 import '../types/types.dart'; | |
| 13 import '../world.dart' show World; | |
| 14 import 'cps_ir_nodes.dart'; | |
| 15 import 'optimizers.dart'; | |
| 16 | |
| 17 /** | |
| 18 * Replaces aggregates with a set of local values. Performs inlining of | |
| 19 * single-use closures to generate more replaceable aggregates. | |
| 20 */ | |
| 21 class ScalarReplacer extends Pass { | |
| 22 String get passName => 'Scalar replacement'; | |
| 23 | |
| 24 final InternalErrorFunction _internalError; | |
| 25 final World _classWorld; | |
| 26 | |
| 27 ScalarReplacer(dart2js.Compiler compiler) | |
| 28 : _internalError = compiler.reporter.internalError, | |
| 29 _classWorld = compiler.world; | |
| 30 | |
| 31 @override | |
| 32 void rewrite(FunctionDefinition root) { | |
| 33 ScalarReplacementVisitor analyzer = | |
| 34 new ScalarReplacementVisitor(_internalError, _classWorld); | |
| 35 analyzer.analyze(root); | |
| 36 analyzer.process(); | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 /** | |
| 41 * Do scalar replacement of aggregates on instances. Since scalar replacement | |
| 42 * can create new candidates, iterate until all scalar replacements are done. | |
| 43 */ | |
| 44 class ScalarReplacementVisitor extends TrampolineRecursiveVisitor { | |
| 45 final InternalErrorFunction internalError; | |
| 46 final World classWorld; | |
| 47 ScalarReplacementRemovalVisitor removalVisitor; | |
| 48 | |
| 49 Primitive _current = null; | |
| 50 Set<Primitive> _allocations = new Set<Primitive>(); | |
| 51 Queue<Primitive> _queue = new Queue<Primitive>(); | |
| 52 | |
| 53 ScalarReplacementVisitor(this.internalError, this.classWorld) { | |
| 54 removalVisitor = new ScalarReplacementRemovalVisitor(this); | |
| 55 } | |
| 56 | |
| 57 void analyze(FunctionDefinition root) { | |
| 58 visit(root); | |
| 59 } | |
| 60 | |
| 61 void process() { | |
| 62 while (_queue.isNotEmpty) { | |
| 63 Primitive allocation = _queue.removeFirst(); | |
| 64 _allocations.remove(allocation); | |
| 65 _current = allocation; | |
| 66 tryScalarReplacement(allocation); | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 void tryScalarReplacement(Primitive allocation) { | |
| 71 // We can do scalar replacement of an aggregate if all uses of an allocation | |
| 72 // are reads or writes. | |
| 73 for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { | |
| 74 Node use = ref.parent; | |
| 75 if (use is GetField) continue; | |
| 76 if (use is SetField && use.objectRef == ref) continue; | |
| 77 return; | |
| 78 } | |
| 79 | |
| 80 Set<FieldElement> reads = new Set<FieldElement>(); | |
| 81 Set<FieldElement> writes = new Set<FieldElement>(); | |
| 82 for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { | |
| 83 Node use = ref.parent; | |
| 84 if (use is GetField) { | |
| 85 reads.add(use.field); | |
| 86 } else if (use is SetField) { | |
| 87 writes.add(use.field); | |
| 88 } else { | |
| 89 assert(false); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 // Find the initial values of the fields. A CreateBox has no initial | |
| 94 // values. CreateInstance has initial values in the order of the fields. | |
| 95 Map<FieldElement, Primitive> fieldInitialValues = | |
| 96 <FieldElement, Primitive>{}; | |
| 97 if (allocation is CreateInstance) { | |
| 98 int i = 0; | |
| 99 allocation.classElement.forEachInstanceField( | |
| 100 (ClassElement enclosingClass, FieldElement field) { | |
| 101 Primitive argument = allocation.argument(i++); | |
| 102 fieldInitialValues[field] = argument; | |
| 103 }, includeSuperAndInjectedMembers: true); | |
| 104 } | |
| 105 | |
| 106 // Create [MutableVariable]s for each written field. Initialize the | |
| 107 // MutableVariable with the value from the allocator, or initialize with a | |
| 108 // `null` constant if there is not initial value. | |
| 109 Map<FieldElement, MutableVariable> cells = | |
| 110 <FieldElement, MutableVariable>{}; | |
| 111 InteriorNode insertionPoint = allocation.parent; // LetPrim | |
| 112 for (FieldElement field in writes) { | |
| 113 MutableVariable variable = new MutableVariable(field); | |
| 114 variable.type = new TypeMask.nonNullEmpty(); | |
| 115 cells[field] = variable; | |
| 116 Primitive initialValue = fieldInitialValues[field]; | |
| 117 if (initialValue == null) { | |
| 118 assert(allocation is CreateBox); | |
| 119 initialValue = new Constant(new NullConstantValue()); | |
| 120 LetPrim let = new LetPrim(initialValue); | |
| 121 let.primitive.parent = let; | |
| 122 insertionPoint = let..insertBelow(insertionPoint); | |
| 123 } | |
| 124 LetMutable let = new LetMutable(variable, initialValue); | |
| 125 let.valueRef.parent = let; | |
| 126 insertionPoint = let..insertBelow(insertionPoint); | |
| 127 } | |
| 128 | |
| 129 // Replace references with MutableVariable operations or references to the | |
| 130 // field's value. | |
| 131 for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { | |
| 132 Node use = ref.parent; | |
| 133 if (use is GetField) { | |
| 134 GetField getField = use; | |
| 135 MutableVariable variable = cells[getField.field]; | |
| 136 if (variable != null) { | |
| 137 GetMutable getter = new GetMutable(variable); | |
| 138 getter.type = getField.type; | |
| 139 getter.variableRef.parent = getter; | |
| 140 getField.replaceUsesWith(getter); | |
| 141 replacePrimitive(getField, getter); | |
| 142 deletePrimitive(getField); | |
| 143 } else { | |
| 144 Primitive value = fieldInitialValues[getField.field]; | |
| 145 getField.replaceUsesWith(value); | |
| 146 deleteLetPrimOf(getField); | |
| 147 } | |
| 148 } else if (use is SetField && use.objectRef == ref) { | |
| 149 SetField setField = use; | |
| 150 MutableVariable variable = cells[setField.field]; | |
| 151 Primitive value = setField.value; | |
| 152 variable.type = variable.type.union(value.type, classWorld); | |
| 153 SetMutable setter = new SetMutable(variable, value); | |
| 154 setter.variableRef.parent = setter; | |
| 155 setter.valueRef.parent = setter; | |
| 156 setField.replaceUsesWith(setter); | |
| 157 replacePrimitive(setField, setter); | |
| 158 deletePrimitive(setField); | |
| 159 } else { | |
| 160 assert(false); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 // Delete [allocation] since that might 'free' another scalar replacement | |
| 165 // candidate by deleting the last non-field-access. | |
| 166 deleteLetPrimOf(allocation); | |
| 167 } | |
| 168 | |
| 169 /// Replaces [old] with [primitive] in [old]'s parent [LetPrim]. | |
| 170 void replacePrimitive(Primitive old, Primitive primitive) { | |
| 171 LetPrim letPrim = old.parent; | |
| 172 letPrim.primitive = primitive; | |
| 173 primitive.parent = letPrim; | |
| 174 } | |
| 175 | |
| 176 void deleteLetPrimOf(Primitive primitive) { | |
| 177 assert(primitive.hasNoUses); | |
| 178 LetPrim letPrim = primitive.parent; | |
| 179 letPrim.remove(); | |
| 180 deletePrimitive(primitive); | |
| 181 } | |
| 182 | |
| 183 void deletePrimitive(Primitive primitive) { | |
| 184 assert(primitive.hasNoUses); | |
| 185 removalVisitor.visit(primitive); | |
| 186 } | |
| 187 | |
| 188 void reconsider(Definition node) { | |
| 189 if (node is CreateInstance || node is CreateBox) { | |
| 190 if (node == _current) return; | |
| 191 enqueue(node); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void enqueue(Primitive node) { | |
| 196 assert(node is CreateInstance || node is CreateBox); | |
| 197 if (_allocations.contains(node)) return; | |
| 198 _allocations.add(node); | |
| 199 _queue.add(node); | |
| 200 } | |
| 201 | |
| 202 // -------------------------- Visitor overrides ------------------------------ | |
| 203 void visitCreateInstance(CreateInstance node) { | |
| 204 enqueue(node); | |
| 205 } | |
| 206 | |
| 207 void visitCreateBox(CreateBox node) { | |
| 208 enqueue(node); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 /// Visit a just-deleted subterm and unlink all [Reference]s in it. Reconsider | |
| 213 /// allocations for scalar replacement. | |
| 214 class ScalarReplacementRemovalVisitor extends TrampolineRecursiveVisitor { | |
| 215 ScalarReplacementVisitor process; | |
| 216 | |
| 217 ScalarReplacementRemovalVisitor(this.process); | |
| 218 | |
| 219 processReference(Reference reference) { | |
| 220 process.reconsider(reference.definition); | |
| 221 reference.unlink(); | |
| 222 } | |
| 223 } | |
| OLD | NEW |