Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/scalar_replacement.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c8cd32d6ca36f8a883ff59fa9f9531fed1510128 |
| --- /dev/null |
| +++ b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart |
| @@ -0,0 +1,244 @@ |
| +// 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. |
| + |
| +import 'optimizers.dart'; |
| + |
| +import 'dart:collection' show Queue; |
| + |
| +import '../closure.dart' show |
| + ClosureClassElement, Identifiers; |
| +import '../common/names.dart' show |
| + Selectors, Identifiers; |
| +import '../compiler.dart' as dart2js show |
| + Compiler; |
| +import '../constants/constant_system.dart'; |
| +import '../constants/values.dart'; |
| +import '../dart_types.dart' as types; |
| +import '../diagnostics/invariant.dart' as dart2js show |
| + InternalErrorFunction; |
| +import '../elements/elements.dart'; |
| +import '../io/source_information.dart' show SourceInformation; |
| +import '../resolution/access_semantics.dart'; |
| +import '../resolution/operators.dart'; |
| +import '../resolution/send_structure.dart'; |
| +import '../tree/tree.dart' as ast; |
| +import '../types/types.dart'; |
| +import '../types/constants.dart' show computeTypeMask; |
| +import '../universe/universe.dart'; |
| +import '../world.dart' show World; |
| +import 'cps_fragment.dart'; |
| +import 'cps_ir_nodes.dart'; |
| +import 'cps_ir_nodes_sexpr.dart' show SExpressionStringifier; |
| + |
| +/** |
| + * Replaces aggregates with a set of local values. Performs inlining of |
| + * single-use closures to generate more replacable aggregates. |
| + */ |
| +class ScalarReplacer extends Pass { |
| + String get passName => 'Scalar replacement'; |
| + |
| + final dart2js.InternalErrorFunction _internalError; |
| + |
| + ScalarReplacer(dart2js.Compiler compiler) |
| + : _internalError = compiler.internalError; |
| + |
| + @override |
| + void rewrite(FunctionDefinition root) { |
| + // Set all parent pointers. |
| + new ParentVisitor().visit(root); |
| + ScalarReplacementVisitor analyzer = |
| + new ScalarReplacementVisitor(_internalError); |
| + analyzer.analyze(root); |
| + analyzer.process(); |
| + } |
| +} |
| + |
| +/** |
| + * Do scalar replacement of aggregates on instances. Since scalar replacement |
| + * can create new candidiates, iterate until all scalar replacements are done. |
| + */ |
| +class ScalarReplacementVisitor extends RecursiveVisitor { |
| + |
| + final dart2js.InternalErrorFunction internalError; |
| + ScalarReplacementRemovalVisitor removalVisitor; |
| + |
| + Primitive _current = null; |
| + Set<Primitive> _allocations = new Set<Primitive>(); |
| + Queue<Primitive> _queue = new Queue<Primitive>(); |
| + |
| + ScalarReplacementVisitor(this.internalError) { |
| + removalVisitor = new ScalarReplacementRemovalVisitor(this); |
| + } |
| + |
| + void analyze(FunctionDefinition root) { |
| + visit(root); |
| + } |
| + |
| + void process() { |
| + while (_queue.isNotEmpty) { |
| + Primitive allocation = _queue.removeFirst(); |
| + _allocations.remove(allocation); |
| + _current = allocation; |
| + tryScalarReplacement(allocation); |
| + } |
| + } |
| + |
| + void tryScalarReplacement(Primitive allocation) { |
| + |
| + // We can do scalar replacement of an aggregate if all uses of an allocation |
| + // are reads or writes. |
| + for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { |
| + Node use = ref.parent; |
| + if (use is GetField) continue; |
| + if (use is SetField && use.object == ref) continue; |
| + return; |
| + } |
| + |
| + Set<FieldElement> reads = new Set<FieldElement>(); |
| + Set<FieldElement> writes = new Set<FieldElement>(); |
| + for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { |
| + Node use = ref.parent; |
| + if (use is GetField) { |
| + reads.add(use.field); |
| + } else { |
| + writes.add(use.field); |
| + } |
| + } |
| + |
| + // Find the initial values of the fields. A CreateBox has no initial |
| + // values. CreateInstance has initial values in the order of the fields. |
| + Map<FieldElement, Primitive> fieldInitialValues = |
| + <FieldElement, Primitive>{}; |
| + if (allocation is CreateInstance) { |
| + int i = 0; |
| + allocation.classElement.forEachInstanceField( |
| + (ClassElement enclosingClass, FieldElement field) { |
| + Primitive argument = allocation.arguments[i++].definition; |
| + fieldInitialValues[field] = argument; |
| + }); |
| + } |
| + |
| + // Create [MutableVariable]s for each written field. Initialize the |
| + // MutableVariable with the value from the allocator, or initialize with a |
| + // `null` constant if there is not initial value. |
| + Map<FieldElement, MutableVariable> cells = <FieldElement, MutableVariable>{}; |
|
asgerf
2015/08/31 09:57:30
long line :(
sra1
2015/09/02 01:12:57
Done.
|
| + InteriorNode insertionPoint = allocation.parent; // LetPrim |
| + for (FieldElement field in writes) { |
| + MutableVariable variable = new MutableVariable(field); |
| + cells[field] = variable; |
| + Primitive initialValue = fieldInitialValues[field]; |
| + if (initialValue == null) { |
| + assert(allocation is CreateBox); |
| + initialValue = new Constant(new NullConstantValue()); |
| + LetPrim let = new LetPrim(initialValue); |
| + let.primitive.parent = let; |
| + insertionPoint = insertAtBody(insertionPoint, let); |
| + } |
| + LetMutable let = new LetMutable(variable, initialValue); |
| + let.value.parent = let; |
| + insertionPoint = insertAtBody(insertionPoint, let); |
| + } |
| + |
| + // Replace references with MutableVariable operations or references to the |
| + // field's value. |
| + for (Reference ref = allocation.firstRef; ref != null; ref = ref.next) { |
| + Node use = ref.parent; |
| + if (use is GetField) { |
| + GetField getField = use; |
| + MutableVariable variable = cells[getField.field]; |
| + if (variable != null) { |
| + GetMutable getter = new GetMutable(variable); |
| + getter.variable.parent = getter; |
| + getter.substituteFor(getField); |
| + getField.parent.primitive = getter; // Replace primitive of LetPrim. |
| + deletePrimitive(getField); |
| + } else { |
| + Primitive value = fieldInitialValues[getField.field]; |
| + value.substituteFor(getField); |
| + deleteLetPrimOf(getField); |
| + } |
| + } else if (use is SetField && use.object == ref) { |
| + SetField setField = use; |
| + MutableVariable variable = cells[setField.field]; |
| + Primitive value = setField.value.definition; |
| + SetMutable setter = new SetMutable(variable, value); |
| + setter.variable.parent = setter; |
| + setter.value.parent = setter; |
| + setter.substituteFor(setField); |
| + setField.parent.primitive = setter; // Replace primitive of LetPrim. |
| + deletePrimitive(setField); |
| + } else { |
| + throw 'unexpected use: $use'; |
| + } |
| + } |
| + |
| + // Delete [allocation] since that might 'free' another scalar replacement |
| + // candidate by deleting the last non-field-access. |
| + deleteLetPrimOf(allocation); |
| + } |
| + |
| + InteriorNode insertAtBody(InteriorNode insertionPoint, InteriorNode let) { |
| + let.parent = insertionPoint; |
| + let.body = insertionPoint.body; |
| + let.body.parent = let; |
| + insertionPoint.body = let; |
| + return let; |
| + } |
| + |
| + void deleteLetPrimOf(Primitive primitive) { |
| + assert(primitive.hasNoUses); |
| + LetPrim letPrim = primitive.parent; |
| + Node child = letPrim.body; |
| + InteriorNode parent = letPrim.parent; |
| + child.parent = parent; |
| + parent.body = child; |
| + |
| + deletePrimitive(primitive); |
| + } |
| + |
| + void deletePrimitive(Primitive primitive) { |
| + assert(primitive.hasNoUses); |
| + removalVisitor.visit(primitive); |
| + } |
| + |
| + void reconsider(Definition node) { |
| + if (node is CreateInstance || node is CreateBox) { |
| + if (node == _current) return; |
| + print('reconsider $node'); |
|
asgerf
2015/08/31 09:57:30
Stray print call
sra1
2015/09/02 01:12:57
Done.
|
| + enqueue(node); |
| + } |
| + } |
| + |
| + void enqueue(Primitive node) { |
| + assert(node is CreateInstance || node is CreateBox); |
| + if (_allocations.contains(node)) return; |
| + _allocations.add(node); |
| + _queue.add(node); |
| + } |
| + |
| + // -------------------------- Visitor overrides ------------------------------ |
| + void visit(Node node) { node.accept(this); } |
|
asgerf
2015/08/31 09:57:30
The visit method is not necessary when extending R
sra1
2015/09/02 01:12:57
Done.
|
| + |
| + void visitCreateInstance(CreateInstance node) { |
| + enqueue(node); |
| + } |
| + |
| + void visitCreateBox(CreateBox node) { |
| + enqueue(node); |
| + } |
| +} |
| + |
| + |
| +/// Visit a just-deleted subterm and unlink all [Reference]s in it. Reconsider |
| +/// allocations for sclara replacement. |
|
asgerf
2015/08/31 09:57:30
sclara -> scalar
sra1
2015/09/02 01:12:57
Done.
|
| +class ScalarReplacementRemovalVisitor extends RecursiveVisitor { |
| + ScalarReplacementVisitor process; |
| + |
| + ScalarReplacementRemovalVisitor(this.process); |
| + |
| + processReference(Reference reference) { |
| + process.reconsider(reference.definition); |
| + reference.unlink(); |
| + } |
| +} |