Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(521)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/optimize_interceptors.dart

Issue 1571433002: dart2js cps: Compute intercepted classes in optimize_interceptors. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: JSArray and JSInt are special Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dart2js.cps_ir.optimize_interceptors; 5 library dart2js.cps_ir.optimize_interceptors;
6 6
7 import 'optimizers.dart'; 7 import 'optimizers.dart';
8 import 'cps_ir_nodes.dart'; 8 import 'cps_ir_nodes.dart';
9 import 'loop_hierarchy.dart'; 9 import 'loop_hierarchy.dart';
10 import 'cps_fragment.dart'; 10 import 'cps_fragment.dart';
11 import '../constants/values.dart'; 11 import '../constants/values.dart';
12 import '../elements/elements.dart'; 12 import '../elements/elements.dart';
13 import '../js_backend/backend_helpers.dart' show BackendHelpers; 13 import '../js_backend/backend_helpers.dart' show BackendHelpers;
14 import '../js_backend/js_backend.dart' show JavaScriptBackend; 14 import '../js_backend/js_backend.dart' show JavaScriptBackend;
15 import '../types/types.dart' show TypeMask; 15 import '../types/types.dart' show TypeMask;
16 import '../io/source_information.dart' show SourceInformation; 16 import '../io/source_information.dart' show SourceInformation;
17 import '../world.dart';
18 import 'type_mask_system.dart';
17 19
18 /// Replaces `getInterceptor` calls with interceptor constants when possible, 20 /// Replaces `getInterceptor` calls with interceptor constants when possible,
19 /// or with "almost constant" expressions like "x && CONST" when the input 21 /// or with "almost constant" expressions like "x && CONST" when the input
20 /// is either null or has a known interceptor. 22 /// is either null or has a known interceptor.
21 // 23 ///
22 // TODO(asgerf): Compute intercepted classes in this pass. 24 /// Narrows the set of intercepted classes for interceptor calls.
25 ///
26 /// Replaces calls on interceptors with one-shot interceptors.
23 class OptimizeInterceptors extends TrampolineRecursiveVisitor implements Pass { 27 class OptimizeInterceptors extends TrampolineRecursiveVisitor implements Pass {
24 String get passName => 'Optimize interceptors'; 28 String get passName => 'Optimize interceptors';
25 29
26 JavaScriptBackend backend; 30 final TypeMaskSystem typeSystem;
31 final JavaScriptBackend backend;
27 LoopHierarchy loopHierarchy; 32 LoopHierarchy loopHierarchy;
28 Continuation currentLoopHeader; 33 Continuation currentLoopHeader;
29 34
30 OptimizeInterceptors(this.backend); 35 OptimizeInterceptors(this.backend, this.typeSystem);
31 36
32 BackendHelpers get helpers => backend.helpers; 37 BackendHelpers get helpers => backend.helpers;
38 World get classWorld => backend.compiler.world;
33 39
34 Map<Interceptor, Continuation> loopHeaderFor = <Interceptor, Continuation>{}; 40 Map<Interceptor, Continuation> loopHeaderFor = <Interceptor, Continuation>{};
35 41
36 void rewrite(FunctionDefinition node) { 42 void rewrite(FunctionDefinition node) {
37 // TODO(asgerf): Computing the LoopHierarchy here may be overkill when all 43 // TODO(asgerf): Computing the LoopHierarchy here may be overkill when all
38 // we want is to hoist constants out of loops. 44 // we want is to hoist constants out of loops.
39 loopHierarchy = new LoopHierarchy(node); 45 loopHierarchy = new LoopHierarchy(node);
40 visit(node.body); 46 visit(node.body);
41 new ShareConstants().visit(node); 47 new ShareConstants().visit(node);
42 } 48 }
43 49
44 @override 50 @override
45 Expression traverseContinuation(Continuation cont) { 51 Expression traverseContinuation(Continuation cont) {
46 Continuation oldLoopHeader = currentLoopHeader; 52 Continuation oldLoopHeader = currentLoopHeader;
47 currentLoopHeader = loopHierarchy.getLoopHeader(cont); 53 currentLoopHeader = loopHierarchy.getLoopHeader(cont);
48 pushAction(() { 54 pushAction(() {
49 currentLoopHeader = oldLoopHeader; 55 currentLoopHeader = oldLoopHeader;
50 }); 56 });
51 return cont.body; 57 return cont.body;
52 } 58 }
53 59
54 /// If only one method table can be returned by the given interceptor,
55 /// returns a constant for that method table.
56 InterceptorConstantValue getInterceptorConstant(Interceptor node) {
57 if (node.interceptedClasses.length == 1 &&
58 node.isInterceptedClassAlwaysExact) {
59 ClassElement interceptorClass = node.interceptedClasses.single;
60 return new InterceptorConstantValue(interceptorClass.rawType);
61 }
62 return null;
63 }
64
65 bool hasNoFalsyValues(ClassElement class_) { 60 bool hasNoFalsyValues(ClassElement class_) {
66 return class_ != helpers.jsInterceptorClass && 61 return class_ != helpers.jsInterceptorClass &&
67 class_ != helpers.jsNullClass && 62 class_ != helpers.jsNullClass &&
68 class_ != helpers.jsBoolClass && 63 class_ != helpers.jsBoolClass &&
69 class_ != helpers.jsStringClass && 64 class_ != helpers.jsStringClass &&
70 !class_.isSubclassOf(helpers.jsNumberClass); 65 !class_.isSubclassOf(helpers.jsNumberClass);
71 } 66 }
72 67
73 Continuation getCurrentOuterLoop({Continuation scope}) { 68 Continuation getCurrentOuterLoop({Continuation scope}) {
74 Continuation inner = null, outer = currentLoopHeader; 69 Continuation inner = null, outer = currentLoopHeader;
(...skipping 21 matching lines...) Expand all
96 Continuation loop = getCurrentOuterLoop(); 91 Continuation loop = getCurrentOuterLoop();
97 if (loop != null) { 92 if (loop != null) {
98 LetCont loopBinding = loop.parent; 93 LetCont loopBinding = loop.parent;
99 letPrim.insertAbove(loopBinding); 94 letPrim.insertAbove(loopBinding);
100 } else { 95 } else {
101 letPrim.insertAbove(useSite); 96 letPrim.insertAbove(useSite);
102 } 97 }
103 return prim; 98 return prim;
104 } 99 }
105 100
101 void computeInterceptedClasses(Interceptor interceptor) {
102 Set<ClassElement> intercepted = interceptor.interceptedClasses;
103 intercepted.clear();
104 for (Reference ref = interceptor.firstRef; ref != null; ref = ref.next) {
105 Node use = ref.parent;
106 if (use is InvokeMethod) {
107 TypeMask type = use.dartReceiver.type;
108 bool includeClass(ClassElement elem) {
sra1 2016/01/08 03:24:04 perhaps change the name to indicate *why* it is in
asgerf 2016/01/08 17:17:48 Done.
109 return classWorld.isInstantiated(elem) &&
110 !typeSystem.areDisjoint(type,
111 typeSystem.getInterceptorSubtypes(elem));
112 }
113 Iterable<ClassElement> classes =
114 backend.getInterceptedClassesOn(use.selector.name);
115 intercepted.addAll(classes.where(includeClass));
116 } else {
117 intercepted.clear();
118 intercepted.add(backend.helpers.jsInterceptorClass);
119 break;
120 }
121 }
122 if (intercepted.contains(backend.helpers.jsInterceptorClass) ||
123 intercepted.contains(backend.helpers.jsNullClass)) {
124 // If the null value is intercepted, update the type of the interceptor.
125 // The Tree IR uses this information to determine if the method lookup
126 // on an InvokeMethod might throw.
127 interceptor.type = interceptor.type.nonNullable();
128 }
129 }
130
131 /// True if [node] may return [JSNumber] instead of [JSInt] or [JSDouble].
132 bool jsNumberClassSuffices(Interceptor node) {
133 // No methods on JSNumber call 'down' to methods on JSInt or JSDouble. If
134 // all uses of the interceptor are for methods is defined only on JSNumber
135 // then JSNumber will suffice in place of choosing between JSInt or
136 // JSDouble.
137 for (Reference ref = node.firstRef; ref != null; ref = ref.next) {
138 if (ref.parent is InvokeMethod) {
139 InvokeMethod invoke = ref.parent;
140 if (invoke.receiver != ref) return false;
141 var interceptedClasses =
142 backend.getInterceptedClassesOn(invoke.selector.name);
143 if (interceptedClasses.contains(helpers.jsDoubleClass)) return false;
144 if (interceptedClasses.contains(helpers.jsIntClass)) return false;
145 continue;
146 }
147 // Other uses need full distinction.
148 return false;
149 }
150 return true;
151 }
152
153 /// True if [node] can intercept a `null` value and return the [JSNull]
154 /// interceptor.
155 bool canInterceptNull(Interceptor node) {
156 for (Reference ref = node.firstRef; ref != null; ref = ref.next) {
157 Node use = ref.parent;
158 if (use is InvokeMethod) {
159 if (selectorsOnNull.contains(use.selector) &&
160 use.dartReceiver.type.isNullable) {
161 return true;
162 }
163 } else {
164 return true;
165 }
166 }
167 return false;
168 }
169
170 /// Returns the only interceptor class that may be returned by [node], or
171 /// `null` if no such class could be found.
172 ClassElement getSingleInterceptorClass(Interceptor node) {
173 // TODO(asgerf): This could be more precise if we used the use-site type,
174 // since the interceptor may have been hoisted out of a loop, where a less
175 // precise type is known.
176 Primitive input = node.input.definition;
177 TypeMask type = input.type;
178 if (canInterceptNull(node)) return null;
179 type = type.nonNullable();
180 if (typeSystem.isDefinitelyArray(type)) {
181 return backend.helpers.jsArrayClass;
182 }
183 if (typeSystem.isDefinitelyInt(type)) {
184 return backend.helpers.jsIntClass;
185 }
186 if (typeSystem.isDefinitelyNum(type) && jsNumberClassSuffices(node)) {
187 return backend.helpers.jsNumberClass;
188 }
189 ClassElement singleClass = type.singleClass(classWorld);
190 if (singleClass != null &&
191 singleClass.isSubclassOf(backend.helpers.jsInterceptorClass)) {
192 return singleClass;
193 }
194 return null;
195 }
196
197 /// Try to replace [interceptor] with a constant, and return `true` if
198 /// successful.
106 bool constifyInterceptor(Interceptor interceptor) { 199 bool constifyInterceptor(Interceptor interceptor) {
107 LetPrim let = interceptor.parent; 200 LetPrim let = interceptor.parent;
108 InterceptorConstantValue constant = getInterceptorConstant(interceptor); 201 Primitive input = interceptor.input.definition;
202 ClassElement classElement = getSingleInterceptorClass(interceptor);
109 203
110 if (constant == null) return false; 204 if (classElement == null) return false;
205 ConstantValue constant = new InterceptorConstantValue(classElement.rawType);
111 206
112 if (interceptor.isAlwaysIntercepted) { 207 if (!input.type.isNullable) {
113 Primitive constantPrim = makeConstantFor(constant, 208 Primitive constantPrim = makeConstantFor(constant,
114 useSite: let, 209 useSite: let,
115 type: interceptor.type, 210 type: interceptor.type,
116 sourceInformation: interceptor.sourceInformation); 211 sourceInformation: interceptor.sourceInformation);
117 constantPrim.useElementAsHint(interceptor.hint); 212 constantPrim.useElementAsHint(interceptor.hint);
118 interceptor..replaceUsesWith(constantPrim)..destroy(); 213 interceptor..replaceUsesWith(constantPrim)..destroy();
119 let.remove(); 214 let.remove();
120 } else if (interceptor.isAlwaysNullOrIntercepted) { 215 } else {
121 Primitive input = interceptor.input.definition;
122 Primitive constantPrim = makeConstantFor(constant, 216 Primitive constantPrim = makeConstantFor(constant,
123 useSite: let, 217 useSite: let,
124 type: interceptor.type.nonNullable(), 218 type: interceptor.type.nonNullable(),
125 sourceInformation: interceptor.sourceInformation); 219 sourceInformation: interceptor.sourceInformation);
126 CpsFragment cps = new CpsFragment(interceptor.sourceInformation); 220 CpsFragment cps = new CpsFragment(interceptor.sourceInformation);
127 Parameter param = new Parameter(interceptor.hint); 221 Parameter param = new Parameter(interceptor.hint);
128 param.type = interceptor.type; 222 param.type = interceptor.type;
129 Continuation cont = cps.letCont(<Parameter>[param]); 223 Continuation cont = cps.letCont(<Parameter>[param]);
130 if (interceptor.interceptedClasses.every(hasNoFalsyValues)) { 224 if (hasNoFalsyValues(classElement)) {
131 // If null is the only falsy value, compile as "x && CONST". 225 // If null is the only falsy value, compile as "x && CONST".
132 cps.ifFalsy(input).invokeContinuation(cont, [input]); 226 cps.ifFalsy(input).invokeContinuation(cont, [input]);
133 } else { 227 } else {
134 // If there are other falsy values compile as "x == null ? x : CONST". 228 // If there are other falsy values compile as "x == null ? x : CONST".
135 Primitive condition = cps.applyBuiltin( 229 Primitive condition = cps.applyBuiltin(
136 BuiltinOperator.LooseEq, 230 BuiltinOperator.LooseEq,
137 [input, cps.makeNull()]); 231 [input, cps.makeNull()]);
138 cps.ifTruthy(condition).invokeContinuation(cont, [input]); 232 cps.ifTruthy(condition).invokeContinuation(cont, [input]);
139 } 233 }
140 cps.invokeContinuation(cont, [constantPrim]); 234 cps.invokeContinuation(cont, [constantPrim]);
141 cps.context = cont; 235 cps.context = cont;
142 cps.insertAbove(let); 236 cps.insertAbove(let);
143 interceptor..replaceUsesWith(param)..destroy(); 237 interceptor..replaceUsesWith(param)..destroy();
144 let.remove(); 238 let.remove();
145 } 239 }
146 return true; 240 return true;
147 } 241 }
148 242
149 @override 243 @override
150 Expression traverseLetPrim(LetPrim node) { 244 Expression traverseLetPrim(LetPrim node) {
151 Expression next = node.body; 245 Expression next = node.body;
152 visit(node.primitive); 246 visit(node.primitive);
153 return next; 247 return next;
154 } 248 }
155 249
156 @override 250 @override
157 void visitInterceptor(Interceptor node) { 251 void visitInterceptor(Interceptor node) {
158 if (constifyInterceptor(node)) return; 252 if (constifyInterceptor(node)) return;
253 computeInterceptedClasses(node);
159 if (node.hasExactlyOneUse) { 254 if (node.hasExactlyOneUse) {
160 // Set the loop header on single-use interceptors so [visitInvokeMethod] 255 // Set the loop header on single-use interceptors so [visitInvokeMethod]
161 // can determine if it should become a one-shot interceptor. 256 // can determine if it should become a one-shot interceptor.
162 loopHeaderFor[node] = currentLoopHeader; 257 loopHeaderFor[node] = currentLoopHeader;
163 } 258 }
164 } 259 }
165 260
166 @override 261 @override
167 void visitInvokeMethod(InvokeMethod node) { 262 void visitInvokeMethod(InvokeMethod node) {
168 if (node.callingConvention != CallingConvention.Intercepted) return; 263 if (node.callingConvention != CallingConvention.Intercepted) return;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 sharedConstantFor.remove(prim.value); 314 sharedConstantFor.remove(prim.value);
220 }); 315 });
221 } 316 }
222 return next; 317 return next;
223 } 318 }
224 319
225 bool shouldShareConstant(Constant constant) { 320 bool shouldShareConstant(Constant constant) {
226 return constant.value.isInterceptor; 321 return constant.value.isInterceptor;
227 } 322 }
228 } 323 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698