Chromium Code Reviews

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

Issue 692513002: Remove old dart2js code. (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.
Jump to:
View unified diff | | 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 // If the interceptor is used by multiple instructions, specialize
171 // it with a set of classes it intercepts.
172 Set<ClassElement> interceptedClasses;
173 JavaScriptBackend backend = compiler.backend;
174 HInstruction dominator = findDominator(node.usedBy);
175 // If there is a call that dominates all other uses, we can use just the
176 // selector of that instruction.
177 if (dominator is HInvokeDynamic &&
178 dominator.isCallOnInterceptor(compiler) &&
179 node == dominator.receiver) {
180 interceptedClasses =
181 backend.getInterceptedClassesOn(dominator.selector.name);
182
183 // If we found that we need number, we must still go through all
184 // uses to check if they require int, or double.
185 if (interceptedClasses.contains(backend.jsNumberClass) &&
186 !(interceptedClasses.contains(backend.jsDoubleClass) ||
187 interceptedClasses.contains(backend.jsIntClass))) {
188 for (HInstruction user in node.usedBy) {
189 if (user is! HInvoke) continue;
190 Set<ClassElement> intercepted =
191 backend.getInterceptedClassesOn(user.selector.name);
192 if (intercepted.contains(backend.jsIntClass)) {
193 interceptedClasses.add(backend.jsIntClass);
194 }
195 if (intercepted.contains(backend.jsDoubleClass)) {
196 interceptedClasses.add(backend.jsDoubleClass);
197 }
198 }
199 }
200 } else {
201 interceptedClasses = new Set<ClassElement>();
202 for (HInstruction user in node.usedBy) {
203 if (user is HInvokeDynamic &&
204 user.isCallOnInterceptor(compiler) &&
205 node == user.receiver) {
206 interceptedClasses.addAll(
207 backend.getInterceptedClassesOn(user.selector.name));
208 } else {
209 // Use a most general interceptor for other instructions, example,
210 // is-checks and escaping interceptors.
211 interceptedClasses.addAll(backend.interceptedClasses);
212 break;
213 }
214 }
215 }
216
217 HInstruction receiver = node.receiver;
218 if (canUseSelfForInterceptor(receiver, interceptedClasses)) {
219 return rewriteToUseSelfAsInterceptor(node, receiver);
220 }
221
222 // Try computing a constant interceptor.
223 HInstruction constantInterceptor =
224 tryComputeConstantInterceptor(receiver, interceptedClasses);
225 if (constantInterceptor != null) {
226 node.block.rewrite(node, constantInterceptor);
227 return false;
228 }
229
230 node.interceptedClasses = interceptedClasses;
231
232 // Try creating a one-shot interceptor.
233 if (node.usedBy.length != 1) return false;
234 if (node.usedBy[0] is !HInvokeDynamic) return false;
235
236 HInvokeDynamic user = node.usedBy[0];
237
238 // If [node] was loop hoisted, we keep the interceptor.
239 if (!user.hasSameLoopHeaderAs(node)) return false;
240
241 // Replace the user with a [HOneShotInterceptor].
242 HConstant nullConstant = graph.addConstantNull(compiler);
243 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs);
244 inputs[0] = nullConstant;
245 HOneShotInterceptor interceptor = new HOneShotInterceptor(
246 user.selector, inputs, user.instructionType, node.interceptedClasses);
247 interceptor.sourcePosition = user.sourcePosition;
248 interceptor.sourceElement = user.sourceElement;
249
250 HBasicBlock block = user.block;
251 block.addAfter(user, interceptor);
252 block.rewrite(user, interceptor);
253 block.remove(user);
254 return true;
255 }
256
257 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) {
258 for (HInstruction user in node.usedBy.toList()) {
259 if (user is HIs) {
260 user.changeUse(node, receiver);
261 } else {
262 // Use the potentially self-argument as new receiver. Note that the
263 // self-argument could potentially have a tighter type than the
264 // receiver which was the input to the interceptor.
265 assert(user.inputs[0] == node);
266 assert(receiver.nonCheck() == user.inputs[1].nonCheck());
267 user.changeUse(node, user.inputs[1]);
268 }
269 }
270 return false;
271 }
272
273 bool visitOneShotInterceptor(HOneShotInterceptor node) {
274 HInstruction constant = tryComputeConstantInterceptor(
275 node.inputs[1], node.interceptedClasses);
276
277 if (constant == null) return false;
278
279 Selector selector = node.selector;
280 HInstruction instruction;
281 if (selector.isGetter) {
282 instruction = new HInvokeDynamicGetter(
283 selector,
284 node.element,
285 <HInstruction>[constant, node.inputs[1]],
286 node.instructionType);
287 } else if (selector.isSetter) {
288 instruction = new HInvokeDynamicSetter(
289 selector,
290 node.element,
291 <HInstruction>[constant, node.inputs[1], node.inputs[2]],
292 node.instructionType);
293 } else {
294 List<HInstruction> inputs = new List<HInstruction>.from(node.inputs);
295 inputs[0] = constant;
296 instruction = new HInvokeDynamicMethod(
297 selector, inputs, node.instructionType, true);
298 }
299
300 HBasicBlock block = node.block;
301 block.addAfter(node, instruction);
302 block.rewrite(node, instruction);
303 return true;
304 }
305 }
OLDNEW

Powered by Google App Engine