Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/share_final_fields.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/share_final_fields.dart b/pkg/compiler/lib/src/cps_ir/share_final_fields.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..36e599e7649299dbaca19dfe2b65993534bf5b8b |
| --- /dev/null |
| +++ b/pkg/compiler/lib/src/cps_ir/share_final_fields.dart |
| @@ -0,0 +1,177 @@ |
| +// 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.share_final_fields; |
| + |
| +import 'optimizers.dart'; |
| +import 'cps_ir_nodes.dart'; |
| +import 'loop_hierarchy.dart'; |
| +import 'cps_fragment.dart'; |
| +import '../constants/values.dart'; |
| +import '../elements/elements.dart'; |
| +import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| +import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| +import '../types/types.dart' show TypeMask; |
| +import '../io/source_information.dart' show SourceInformation; |
| + |
| +/// Removes redundant GetField operations. |
| +/// |
| +/// The pass performs these optimizations for field loads. |
| +///- share GetFields of final fields when one is in scope of the other. |
| +///- pull GetField operations out of loops. |
| +class ShareFinalFields extends TrampolineRecursiveVisitor implements Pass { |
| + String get passName => 'Share final fields'; |
| + |
| + /// The innermost loop containing a given primitive. |
| + final Map<Primitive, Continuation> loopHeaderFor = |
| + <Primitive, Continuation>{}; |
| + |
| + /// An interceptor currently in scope for a given primitive. |
| + final Map<Primitive, Interceptor> interceptorFor = <Primitive, Interceptor>{}; |
|
asgerf
2015/10/30 09:57:46
Unused field
|
| + |
| + // field -> receiver -> GetField |
| + Map<FieldElement, Map<Primitive, Primitive>> fieldValues = |
| + <FieldElement, Map<Primitive, Primitive>>{}; |
| + |
| + /// Interceptors that have been hoisted out of a given loop. |
| + final Map<Continuation, List<GetField>> loopHoistedGetters = |
| + <Continuation, List<GetField>>{}; |
| + |
| + JavaScriptBackend backend; |
| + LoopHierarchy loopHierarchy; |
| + Continuation currentLoopHeader; |
| + |
| + ShareFinalFields(this.backend); |
| + |
| + BackendHelpers get helpers => backend.helpers; |
| + |
| + void rewrite(FunctionDefinition node) { |
| + loopHierarchy = new LoopHierarchy(node); |
| + visit(node.body); |
| + } |
| + |
| + @override |
| + Expression traverseContinuation(Continuation cont) { |
| + Continuation oldLoopHeader = currentLoopHeader; |
| + currentLoopHeader = loopHierarchy.getLoopHeader(cont); |
| + for (Parameter parameter in cont.parameters) { |
| + loopHeaderFor[parameter] = currentLoopHeader; |
| + } |
| + if (cont.isRecursive) { |
| + pushAction(() { |
| + // After the loop body has been processed, all values hoisted to this |
| + // loop fall out of scope and should be removed from the environment. |
| + List<GetField> hoisted = loopHoistedGetters[cont]; |
| + if (hoisted != null) { |
| + for (GetField primitive in hoisted) { |
| + Primitive refinedReceiver = primitive.object.definition; |
| + Primitive receiver = refinedReceiver.effectiveDefinition; |
| + var map = fieldValues[primitive.field]; |
| + assert(map[receiver] == primitive); |
| + map.remove(receiver); |
| + } |
| + } |
| + }); |
| + } |
| + pushAction(() { |
| + currentLoopHeader = oldLoopHeader; |
| + }); |
| + return cont.body; |
| + } |
| + |
| + Continuation getCurrentOuterLoop({Continuation scope}) { |
| + Continuation inner = null, outer = currentLoopHeader; |
| + while (outer != scope) { |
| + inner = outer; |
| + outer = loopHierarchy.getEnclosingLoop(outer); |
| + } |
| + return inner; |
| + } |
| + |
| + @override |
| + Expression traverseLetPrim(LetPrim node) { |
| + loopHeaderFor[node.primitive] = currentLoopHeader; |
| + Expression next = node.body; |
| + if (node.primitive is! GetField) { |
| + return next; |
| + } |
| + GetField primitive = node.primitive; |
| + FieldElement field = primitive.field; |
| + |
| + if (!shouldShareField(field)) { |
| + return next; |
| + } |
| + |
| + Primitive refinedReceiver = primitive.object.definition; |
| + Primitive receiver = refinedReceiver.effectiveDefinition; |
| + |
| + // Try to reuse an existing load for the same input. |
| + var map = fieldValues.putIfAbsent(field, () => <Primitive,Primitive>{}); |
| + Primitive existing = map[receiver]; |
| + if (existing != null) { |
| + existing.substituteFor(primitive); |
| + primitive.destroy(); |
| + node.remove(); |
| + return next; |
| + } |
| + |
| + map[receiver] = primitive; |
| + |
| + if (primitive.objectIsNotNull) { |
| + // Determine how far the GetField can be lifted. The outermost loop that |
| + // contains the input binding should also contain the load. |
| + // Don't move above a refinement guard since that might be unsafe. |
| + // TODO(sra): We can move above a refinement guard provided the input is |
| + // still safe (non-null). |
| + Continuation referencedLoop = |
| + lowestCommonAncestor(loopHeaderFor[refinedReceiver], |
| + currentLoopHeader); |
| + if (referencedLoop != currentLoopHeader) { |
| + Continuation hoistTarget = getCurrentOuterLoop(scope: referencedLoop); |
| + LetCont loopBinding = hoistTarget.parent; |
| + node.remove(); |
| + node.insertAbove(loopBinding); |
| + // Remove the hoisted operations from the environment after processing |
| + // the loop. |
| + loopHoistedGetters |
| + .putIfAbsent(hoistTarget, () => <GetField>[]) |
| + .add(primitive); |
| + return next; |
| + } |
| + } |
| + |
| + pushAction(() { |
| + var map = fieldValues[field]; |
| + assert(map[receiver] == primitive); |
| + map.remove(receiver); |
| + }); |
| + return next; |
| + } |
| + |
| + bool shouldShareField(FieldElement field) { |
| + return backend.compiler.world.fieldNeverChanges(field); |
| + } |
| + |
| + |
| + /// Returns the the innermost loop that effectively encloses both |
| + /// c1 and c2 (or `null` if there is no such loop). |
| + Continuation lowestCommonAncestor(Continuation c1, Continuation c2) { |
| + int d1 = getDepth(c1), d2 = getDepth(c2); |
| + while (c1 != c2) { |
| + if (d1 <= d2) { |
| + c2 = loopHierarchy.getEnclosingLoop(c2); |
| + d2 = getDepth(c2); |
| + } else { |
| + c1 = loopHierarchy.getEnclosingLoop(c1); |
| + d1 = getDepth(c1); |
| + } |
| + } |
| + return c1; |
| + } |
| + |
| + int getDepth(Continuation loop) { |
| + if (loop == null) return -1; |
| + return loopHierarchy.loopDepth[loop]; |
| + } |
| +} |