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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 part of ssa;
6
7 /**
8 * This phase simplifies interceptors in multiple ways:
9 *
10 * 1) If the interceptor is for an object whose type is known, it
11 * tries to use a constant interceptor instead.
12 *
13 * 2) It specializes interceptors based on the selectors it is being
14 * called with.
15 *
16 * 3) If we know the object is not intercepted, we just use it
17 * instead.
18 *
19 * 4) It replaces all interceptors that are used only once with
20 * one-shot interceptors. It saves code size and makes the receiver of
21 * an intercepted call a candidate for being generated at use site.
22 *
23 */
24 class SsaSimplifyInterceptors extends HBaseVisitor
25 implements OptimizationPhase {
26 final String name = "SsaSimplifyInterceptors";
27 final ConstantSystem constantSystem;
28 final Compiler compiler;
29 final CodegenWorkItem work;
30 HGraph graph;
31
32 SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.work);
33
34 void visitGraph(HGraph graph) {
35 this.graph = graph;
36 visitDominatorTree(graph);
37 }
38
39 void visitBasicBlock(HBasicBlock node) {
40 currentBlock = node;
41
42 HInstruction instruction = node.first;
43 while (instruction != null) {
44 bool shouldRemove = instruction.accept(this);
45 HInstruction next = instruction.next;
46 if (shouldRemove) {
47 instruction.block.remove(instruction);
48 }
49 instruction = next;
50 }
51 }
52
53 bool visitInstruction(HInstruction instruction) => false;
54
55 bool visitInvoke(HInvoke invoke) {
56 if (!invoke.isInterceptedCall) return false;
57 var interceptor = invoke.inputs[0];
58 if (interceptor is! HInterceptor) return false;
59 HInstruction constant = tryComputeConstantInterceptor(
60 invoke.inputs[1], interceptor.interceptedClasses);
61 if (constant != null) {
62 invoke.changeUse(interceptor, constant);
63 }
64 return false;
65 }
66
67 bool canUseSelfForInterceptor(HInstruction receiver,
68 Set<ClassElement> interceptedClasses) {
69 JavaScriptBackend backend = compiler.backend;
70 ClassWorld classWorld = compiler.world;
71
72 if (receiver.canBePrimitive(compiler)) {
73 // Primitives always need interceptors.
74 return false;
75 }
76 if (receiver.canBeNull() &&
77 interceptedClasses.contains(backend.jsNullClass)) {
78 // Need the JSNull interceptor.
79 return false;
80 }
81
82 // All intercepted classes extend `Interceptor`, so if the receiver can't be
83 // a class extending `Interceptor` then it can be called directly.
84 return new TypeMask.nonNullSubclass(backend.jsInterceptorClass, classWorld)
85 .intersection(receiver.instructionType, classWorld)
86 .isEmpty;
87 }
88
89 HInstruction tryComputeConstantInterceptor(
90 HInstruction input,
91 Set<ClassElement> interceptedClasses) {
92 if (input == graph.explicitReceiverParameter) {
93 // If `explicitReceiverParameter` is set it means the current method is an
94 // interceptor method, and `this` is the interceptor. The caller just did
95 // `getInterceptor(foo).currentMethod(foo)` to enter the current method.
96 return graph.thisInstruction;
97 }
98
99 ClassElement constantInterceptor;
100 ClassWorld classWorld = compiler.world;
101 JavaScriptBackend backend = compiler.backend;
102 if (input.canBeNull()) {
103 if (input.isNull()) {
104 constantInterceptor = backend.jsNullClass;
105 }
106 } else if (input.isInteger(compiler)) {
107 constantInterceptor = backend.jsIntClass;
108 } else if (input.isDouble(compiler)) {
109 constantInterceptor = backend.jsDoubleClass;
110 } else if (input.isBoolean(compiler)) {
111 constantInterceptor = backend.jsBoolClass;
112 } else if (input.isString(compiler)) {
113 constantInterceptor = backend.jsStringClass;
114 } else if (input.isArray(compiler)) {
115 constantInterceptor = backend.jsArrayClass;
116 } else if (input.isNumber(compiler) &&
117 !interceptedClasses.contains(backend.jsIntClass) &&
118 !interceptedClasses.contains(backend.jsDoubleClass)) {
119 // If the method being intercepted is not defined in [int] or [double] we
120 // can safely use the number interceptor. This is because none of the
121 // [int] or [double] methods are called from a method defined on [num].
122 constantInterceptor = backend.jsNumberClass;
123 } else {
124 // Try to find constant interceptor for a native class. If the receiver
125 // is constrained to a leaf native class, we can use the class's
126 // interceptor directly.
127
128 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf
129 // classes. When the receiver type is not a leaf class, we might still be
130 // able to use the receiver class as a constant interceptor. It is
131 // usually the case that methods defined on a non-leaf class don't test
132 // for a subclass or call methods defined on a subclass. Provided the
133 // code is completely insensitive to the specific instance subclasses, we
134 // can use the non-leaf class directly.
135 ClassElement element = input.instructionType.singleClass(classWorld);
136 if (element != null && element.isNative) {
137 constantInterceptor = element;
138 }
139 }
140
141 if (constantInterceptor == null) return null;
142
143 // If we just happen to be in an instance method of the constant
144 // interceptor, `this` is a shorter alias.
145 if (constantInterceptor == work.element.enclosingClass &&
146 graph.thisInstruction != null) {
147 return graph.thisInstruction;
148 }
149
150 ConstantValue constant =
151 new InterceptorConstantValue(constantInterceptor.thisType);
152 return graph.addConstant(constant, compiler);
153 }
154
155 HInstruction findDominator(Iterable<HInstruction> instructions) {
156 HInstruction result;
157 L1: for (HInstruction candidate in instructions) {
158 for (HInstruction current in instructions) {
159 if (current != candidate && !candidate.dominates(current)) continue L1;
160 }
161 result = candidate;
162 break;
163 }
164 return result;
165 }
166
167 bool visitInterceptor(HInterceptor node) {
168 if (node.isConstant()) return false;
169
170 // Specialize the interceptor with set of classes it intercepts, considering
171 // all uses. (The specialized interceptor has a shorter dispatch chain).
172 // This operation applies only where the interceptor is used to dispatch a
173 // method. Other uses, e.g. as an ordinary argument or a HIs check use the
174 // most general interceptor.
175 //
176 // TODO(sra): Take into account the receiver type at each call. e.g:
177 //
178 // (a) => a.length + a.hashCode
179 //
180 // Currently we use the most general interceptor since all intercepted types
181 // implement `hashCode`. But in this example, `a.hashCode` is only reached
182 // if `a.length` succeeds, which is indicated by the hashCode receiver being
183 // a HTypeKnown instruction.
184
185 int useCount(HInstruction user, HInstruction used) =>
186 user.inputs.where((input) => input == used).length;
187
188 Set<ClassElement> interceptedClasses;
189 JavaScriptBackend backend = compiler.backend;
190 HInstruction dominator = findDominator(node.usedBy);
191 // If there is a call that dominates all other uses, we can use just the
192 // selector of that instruction.
193 if (dominator is HInvokeDynamic &&
194 dominator.isCallOnInterceptor(compiler) &&
195 node == dominator.receiver &&
196 useCount(dominator, node) == 1) {
197 interceptedClasses =
198 backend.getInterceptedClassesOn(dominator.selector.name);
199
200 // If we found that we need number, we must still go through all
201 // uses to check if they require int, or double.
202 if (interceptedClasses.contains(backend.jsNumberClass) &&
203 !(interceptedClasses.contains(backend.jsDoubleClass) ||
204 interceptedClasses.contains(backend.jsIntClass))) {
205 for (HInstruction user in node.usedBy) {
206 if (user is! HInvoke) continue;
207 Set<ClassElement> intercepted =
208 backend.getInterceptedClassesOn(user.selector.name);
209 if (intercepted.contains(backend.jsIntClass)) {
210 interceptedClasses.add(backend.jsIntClass);
211 }
212 if (intercepted.contains(backend.jsDoubleClass)) {
213 interceptedClasses.add(backend.jsDoubleClass);
214 }
215 }
216 }
217 } else {
218 interceptedClasses = new Set<ClassElement>();
219 for (HInstruction user in node.usedBy) {
220 if (user is HInvokeDynamic &&
221 user.isCallOnInterceptor(compiler) &&
222 node == user.receiver &&
223 useCount(user, node) == 1) {
224 interceptedClasses.addAll(
225 backend.getInterceptedClassesOn(user.selector.name));
226 } else if (user is HInvokeSuper &&
227 user.isCallOnInterceptor(compiler) &&
228 node == user.receiver &&
229 useCount(user, node) == 1) {
230 interceptedClasses.addAll(
231 backend.getInterceptedClassesOn(user.selector.name));
232 } else {
233 // Use a most general interceptor for other instructions, example,
234 // is-checks and escaping interceptors.
235 interceptedClasses.addAll(backend.interceptedClasses);
236 break;
237 }
238 }
239 }
240
241 HInstruction receiver = node.receiver;
242
243 if (canUseSelfForInterceptor(receiver, interceptedClasses)) {
244 return rewriteToUseSelfAsInterceptor(node, receiver);
245 }
246
247 // Try computing a constant interceptor.
248 HInstruction constantInterceptor =
249 tryComputeConstantInterceptor(receiver, interceptedClasses);
250 if (constantInterceptor != null) {
251 node.block.rewrite(node, constantInterceptor);
252 return false;
253 }
254
255 node.interceptedClasses = interceptedClasses;
256
257 // Try creating a one-shot interceptor.
258 if (node.usedBy.length != 1) return false;
259 if (node.usedBy[0] is !HInvokeDynamic) return false;
260
261 HInvokeDynamic user = node.usedBy[0];
262
263 // If [node] was loop hoisted, we keep the interceptor.
264 if (!user.hasSameLoopHeaderAs(node)) return false;
265
266 // Replace the user with a [HOneShotInterceptor].
267 HConstant nullConstant = graph.addConstantNull(compiler);
268 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs);
269 inputs[0] = nullConstant;
270 HOneShotInterceptor interceptor = new HOneShotInterceptor(
271 user.selector, inputs, user.instructionType, node.interceptedClasses);
272 interceptor.sourcePosition = user.sourcePosition;
273 interceptor.sourceElement = user.sourceElement;
274
275 HBasicBlock block = user.block;
276 block.addAfter(user, interceptor);
277 block.rewrite(user, interceptor);
278 block.remove(user);
279 return true;
280 }
281
282 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) {
283 for (HInstruction user in node.usedBy.toList()) {
284 if (user is HIs) {
285 user.changeUse(node, receiver);
286 } else {
287 // Use the potentially self-argument as new receiver. Note that the
288 // self-argument could potentially have a tighter type than the
289 // receiver which was the input to the interceptor.
290 assert(user.inputs[0] == node);
291 assert(receiver.nonCheck() == user.inputs[1].nonCheck());
292 user.changeUse(node, user.inputs[1]);
293 }
294 }
295 return false;
296 }
297
298 bool visitOneShotInterceptor(HOneShotInterceptor node) {
299 HInstruction constant = tryComputeConstantInterceptor(
300 node.inputs[1], node.interceptedClasses);
301
302 if (constant == null) return false;
303
304 Selector selector = node.selector;
305 HInstruction instruction;
306 if (selector.isGetter) {
307 instruction = new HInvokeDynamicGetter(
308 selector,
309 node.element,
310 <HInstruction>[constant, node.inputs[1]],
311 node.instructionType);
312 } else if (selector.isSetter) {
313 instruction = new HInvokeDynamicSetter(
314 selector,
315 node.element,
316 <HInstruction>[constant, node.inputs[1], node.inputs[2]],
317 node.instructionType);
318 } else {
319 List<HInstruction> inputs = new List<HInstruction>.from(node.inputs);
320 inputs[0] = constant;
321 instruction = new HInvokeDynamicMethod(
322 selector, inputs, node.instructionType, true);
323 }
324
325 HBasicBlock block = node.block;
326 block.addAfter(node, instruction);
327 block.rewrite(node, instruction);
328 return true;
329 }
330 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698