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 |
| 5 library dart2js.cps_ir.share_final_fields; |
| 6 |
| 7 import 'optimizers.dart'; |
| 8 import 'cps_ir_nodes.dart'; |
| 9 import 'loop_hierarchy.dart'; |
| 10 import '../elements/elements.dart'; |
| 11 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 12 import '../types/types.dart' show TypeMask; |
| 13 |
| 14 /// Removes redundant GetField operations. |
| 15 /// |
| 16 /// The pass performs these optimizations for field loads: |
| 17 /// - share GetFields of final fields when one is in scope of the other. |
| 18 /// - pull GetField operations of final fields out of loops when safe (object is |
| 19 /// not null). |
| 20 /// |
| 21 /// This pass only optimizes final fields and should be replaced with a full |
| 22 /// load elimination pass. |
| 23 class ShareFinalFields extends TrampolineRecursiveVisitor implements Pass { |
| 24 String get passName => 'Share final fields'; |
| 25 |
| 26 /// The innermost loop containing a given primitive. |
| 27 final Map<Primitive, Continuation> loopHeaderFor = |
| 28 <Primitive, Continuation>{}; |
| 29 |
| 30 // field -> receiver -> GetField |
| 31 Map<FieldElement, Map<Primitive, Primitive>> fieldValues = |
| 32 <FieldElement, Map<Primitive, Primitive>>{}; |
| 33 |
| 34 /// Interceptors that have been hoisted out of a given loop. |
| 35 final Map<Continuation, List<GetField>> loopHoistedGetters = |
| 36 <Continuation, List<GetField>>{}; |
| 37 |
| 38 JavaScriptBackend backend; |
| 39 LoopHierarchy loopHierarchy; |
| 40 Continuation currentLoopHeader; |
| 41 |
| 42 ShareFinalFields(this.backend); |
| 43 |
| 44 void rewrite(FunctionDefinition node) { |
| 45 loopHierarchy = new LoopHierarchy(node); |
| 46 visit(node.body); |
| 47 } |
| 48 |
| 49 @override |
| 50 Expression traverseContinuation(Continuation cont) { |
| 51 Continuation oldLoopHeader = currentLoopHeader; |
| 52 currentLoopHeader = loopHierarchy.getLoopHeader(cont); |
| 53 for (Parameter parameter in cont.parameters) { |
| 54 loopHeaderFor[parameter] = currentLoopHeader; |
| 55 } |
| 56 if (cont.isRecursive) { |
| 57 pushAction(() { |
| 58 // After the loop body has been processed, all values hoisted to this |
| 59 // loop fall out of scope and should be removed from the environment. |
| 60 List<GetField> hoisted = loopHoistedGetters[cont]; |
| 61 if (hoisted != null) { |
| 62 for (GetField primitive in hoisted) { |
| 63 Primitive refinedReceiver = primitive.object.definition; |
| 64 Primitive receiver = refinedReceiver.effectiveDefinition; |
| 65 var map = fieldValues[primitive.field]; |
| 66 assert(map[receiver] == primitive); |
| 67 map.remove(receiver); |
| 68 } |
| 69 } |
| 70 }); |
| 71 } |
| 72 pushAction(() { |
| 73 currentLoopHeader = oldLoopHeader; |
| 74 }); |
| 75 return cont.body; |
| 76 } |
| 77 |
| 78 Continuation getCurrentOuterLoop({Continuation scope}) { |
| 79 Continuation inner = null, outer = currentLoopHeader; |
| 80 while (outer != scope) { |
| 81 inner = outer; |
| 82 outer = loopHierarchy.getEnclosingLoop(outer); |
| 83 } |
| 84 return inner; |
| 85 } |
| 86 |
| 87 @override |
| 88 Expression traverseLetPrim(LetPrim node) { |
| 89 loopHeaderFor[node.primitive] = currentLoopHeader; |
| 90 Expression next = node.body; |
| 91 if (node.primitive is! GetField) { |
| 92 return next; |
| 93 } |
| 94 GetField primitive = node.primitive; |
| 95 FieldElement field = primitive.field; |
| 96 |
| 97 if (!shouldShareField(field)) { |
| 98 return next; |
| 99 } |
| 100 |
| 101 Primitive refinedReceiver = primitive.object.definition; |
| 102 Primitive receiver = refinedReceiver.effectiveDefinition; |
| 103 |
| 104 // Try to reuse an existing load for the same input. |
| 105 var map = fieldValues.putIfAbsent(field, () => <Primitive,Primitive>{}); |
| 106 Primitive existing = map[receiver]; |
| 107 if (existing != null) { |
| 108 existing.substituteFor(primitive); |
| 109 primitive.destroy(); |
| 110 node.remove(); |
| 111 return next; |
| 112 } |
| 113 |
| 114 map[receiver] = primitive; |
| 115 |
| 116 if (primitive.objectIsNotNull) { |
| 117 // Determine how far the GetField can be lifted. The outermost loop that |
| 118 // contains the input binding should also contain the load. |
| 119 // Don't move above a refinement guard since that might be unsafe. |
| 120 |
| 121 // TODO(sra): We can move above a refinement guard provided the input is |
| 122 // still in scope and safe (non-null). We will have to replace |
| 123 // primitive.object with the most constrained refinement still in scope. |
| 124 Continuation referencedLoop = |
| 125 lowestCommonAncestor(loopHeaderFor[refinedReceiver], |
| 126 currentLoopHeader); |
| 127 if (referencedLoop != currentLoopHeader) { |
| 128 Continuation hoistTarget = getCurrentOuterLoop(scope: referencedLoop); |
| 129 LetCont loopBinding = hoistTarget.parent; |
| 130 node.remove(); |
| 131 node.insertAbove(loopBinding); |
| 132 // Remove the hoisted operations from the environment after processing |
| 133 // the loop. |
| 134 loopHoistedGetters |
| 135 .putIfAbsent(hoistTarget, () => <GetField>[]) |
| 136 .add(primitive); |
| 137 return next; |
| 138 } |
| 139 } |
| 140 |
| 141 pushAction(() { |
| 142 var map = fieldValues[field]; |
| 143 assert(map[receiver] == primitive); |
| 144 map.remove(receiver); |
| 145 }); |
| 146 return next; |
| 147 } |
| 148 |
| 149 bool shouldShareField(FieldElement field) { |
| 150 return backend.compiler.world.fieldNeverChanges(field); |
| 151 } |
| 152 |
| 153 /// Returns the the innermost loop that effectively encloses both |
| 154 /// c1 and c2 (or `null` if there is no such loop). |
| 155 Continuation lowestCommonAncestor(Continuation c1, Continuation c2) { |
| 156 int d1 = getDepth(c1), d2 = getDepth(c2); |
| 157 while (c1 != c2) { |
| 158 if (d1 <= d2) { |
| 159 c2 = loopHierarchy.getEnclosingLoop(c2); |
| 160 d2 = getDepth(c2); |
| 161 } else { |
| 162 c1 = loopHierarchy.getEnclosingLoop(c1); |
| 163 d1 = getDepth(c1); |
| 164 } |
| 165 } |
| 166 return c1; |
| 167 } |
| 168 |
| 169 int getDepth(Continuation loop) { |
| 170 if (loop == null) return -1; |
| 171 return loopHierarchy.loopDepth[loop]; |
| 172 } |
| 173 } |
OLD | NEW |